109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2f7018c21STomi Valkeinen /* 3f7018c21STomi Valkeinen * Frame buffer driver for Trident TGUI, Blade and Image series 4f7018c21STomi Valkeinen * 5f7018c21STomi Valkeinen * Copyright 2001, 2002 - Jani Monoses <jani@iv.ro> 6f7018c21STomi Valkeinen * Copyright 2009 Krzysztof Helt <krzysztof.h1@wp.pl> 7f7018c21STomi Valkeinen * 8f7018c21STomi Valkeinen * CREDITS:(in order of appearance) 9f7018c21STomi Valkeinen * skeletonfb.c by Geert Uytterhoeven and other fb code in drivers/video 10f7018c21STomi Valkeinen * Special thanks ;) to Mattia Crivellini <tia@mclink.it> 11f7018c21STomi Valkeinen * much inspired by the XFree86 4.x Trident driver sources 12f7018c21STomi Valkeinen * by Alan Hourihane the FreeVGA project 13f7018c21STomi Valkeinen * Francesco Salvestrini <salvestrini@users.sf.net> XP support, 14f7018c21STomi Valkeinen * code, suggestions 15f7018c21STomi Valkeinen * TODO: 16f7018c21STomi Valkeinen * timing value tweaking so it looks good on every monitor in every mode 17f7018c21STomi Valkeinen */ 18f7018c21STomi Valkeinen 19*145eed48SThomas Zimmermann #include <linux/aperture.h> 20f7018c21STomi Valkeinen #include <linux/module.h> 21f7018c21STomi Valkeinen #include <linux/fb.h> 22f7018c21STomi Valkeinen #include <linux/init.h> 23f7018c21STomi Valkeinen #include <linux/pci.h> 24f7018c21STomi Valkeinen #include <linux/slab.h> 25f7018c21STomi Valkeinen 26f7018c21STomi Valkeinen #include <linux/delay.h> 27f7018c21STomi Valkeinen #include <video/vga.h> 28f7018c21STomi Valkeinen #include <video/trident.h> 29f7018c21STomi Valkeinen 306a5e3bd0SOndrej Zary #include <linux/i2c.h> 316a5e3bd0SOndrej Zary #include <linux/i2c-algo-bit.h> 326a5e3bd0SOndrej Zary 33f7018c21STomi Valkeinen struct tridentfb_par { 34f7018c21STomi Valkeinen void __iomem *io_virt; /* iospace virtual memory address */ 35f7018c21STomi Valkeinen u32 pseudo_pal[16]; 36f7018c21STomi Valkeinen int chip_id; 37f7018c21STomi Valkeinen int flatpanel; 38f7018c21STomi Valkeinen void (*init_accel) (struct tridentfb_par *, int, int); 39f7018c21STomi Valkeinen void (*wait_engine) (struct tridentfb_par *); 40f7018c21STomi Valkeinen void (*fill_rect) 41f7018c21STomi Valkeinen (struct tridentfb_par *par, u32, u32, u32, u32, u32, u32); 42f7018c21STomi Valkeinen void (*copy_rect) 43f7018c21STomi Valkeinen (struct tridentfb_par *par, u32, u32, u32, u32, u32, u32); 44f7018c21STomi Valkeinen void (*image_blit) 45f7018c21STomi Valkeinen (struct tridentfb_par *par, const char*, 46f7018c21STomi Valkeinen u32, u32, u32, u32, u32, u32); 47f7018c21STomi Valkeinen unsigned char eng_oper; /* engine operation... */ 486a5e3bd0SOndrej Zary bool ddc_registered; 496a5e3bd0SOndrej Zary struct i2c_adapter ddc_adapter; 506a5e3bd0SOndrej Zary struct i2c_algo_bit_data ddc_algo; 51f7018c21STomi Valkeinen }; 52f7018c21STomi Valkeinen 53f7018c21STomi Valkeinen static struct fb_fix_screeninfo tridentfb_fix = { 54f7018c21STomi Valkeinen .id = "Trident", 55f7018c21STomi Valkeinen .type = FB_TYPE_PACKED_PIXELS, 56f7018c21STomi Valkeinen .ypanstep = 1, 57f7018c21STomi Valkeinen .visual = FB_VISUAL_PSEUDOCOLOR, 58f7018c21STomi Valkeinen .accel = FB_ACCEL_NONE, 59f7018c21STomi Valkeinen }; 60f7018c21STomi Valkeinen 61f7018c21STomi Valkeinen /* defaults which are normally overriden by user values */ 62f7018c21STomi Valkeinen 63f7018c21STomi Valkeinen /* video mode */ 646a5e3bd0SOndrej Zary static char *mode_option; 65f7018c21STomi Valkeinen static int bpp = 8; 66f7018c21STomi Valkeinen 67f7018c21STomi Valkeinen static int noaccel; 68f7018c21STomi Valkeinen 69f7018c21STomi Valkeinen static int center; 70f7018c21STomi Valkeinen static int stretch; 71f7018c21STomi Valkeinen 72f7018c21STomi Valkeinen static int fp; 73f7018c21STomi Valkeinen static int crt; 74f7018c21STomi Valkeinen 75f7018c21STomi Valkeinen static int memsize; 76f7018c21STomi Valkeinen static int memdiff; 77f7018c21STomi Valkeinen static int nativex; 78f7018c21STomi Valkeinen 79f7018c21STomi Valkeinen module_param(mode_option, charp, 0); 80f7018c21STomi Valkeinen MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'"); 81f7018c21STomi Valkeinen module_param_named(mode, mode_option, charp, 0); 82f7018c21STomi Valkeinen MODULE_PARM_DESC(mode, "Initial video mode e.g. '648x480-8@60' (deprecated)"); 83f7018c21STomi Valkeinen module_param(bpp, int, 0); 84f7018c21STomi Valkeinen module_param(center, int, 0); 85f7018c21STomi Valkeinen module_param(stretch, int, 0); 86f7018c21STomi Valkeinen module_param(noaccel, int, 0); 87f7018c21STomi Valkeinen module_param(memsize, int, 0); 88f7018c21STomi Valkeinen module_param(memdiff, int, 0); 89f7018c21STomi Valkeinen module_param(nativex, int, 0); 90f7018c21STomi Valkeinen module_param(fp, int, 0); 91f7018c21STomi Valkeinen MODULE_PARM_DESC(fp, "Define if flatpanel is connected"); 92f7018c21STomi Valkeinen module_param(crt, int, 0); 93f7018c21STomi Valkeinen MODULE_PARM_DESC(crt, "Define if CRT is connected"); 94f7018c21STomi Valkeinen 95f7018c21STomi Valkeinen static inline int is_oldclock(int id) 96f7018c21STomi Valkeinen { 97f7018c21STomi Valkeinen return (id == TGUI9440) || 98f7018c21STomi Valkeinen (id == TGUI9660) || 99f7018c21STomi Valkeinen (id == CYBER9320); 100f7018c21STomi Valkeinen } 101f7018c21STomi Valkeinen 102f7018c21STomi Valkeinen static inline int is_oldprotect(int id) 103f7018c21STomi Valkeinen { 104f7018c21STomi Valkeinen return is_oldclock(id) || 105f7018c21STomi Valkeinen (id == PROVIDIA9685) || 106f7018c21STomi Valkeinen (id == CYBER9382) || 107f7018c21STomi Valkeinen (id == CYBER9385); 108f7018c21STomi Valkeinen } 109f7018c21STomi Valkeinen 110f7018c21STomi Valkeinen static inline int is_blade(int id) 111f7018c21STomi Valkeinen { 112f7018c21STomi Valkeinen return (id == BLADE3D) || 113f7018c21STomi Valkeinen (id == CYBERBLADEE4) || 114f7018c21STomi Valkeinen (id == CYBERBLADEi7) || 115f7018c21STomi Valkeinen (id == CYBERBLADEi7D) || 116f7018c21STomi Valkeinen (id == CYBERBLADEi1) || 117f7018c21STomi Valkeinen (id == CYBERBLADEi1D) || 118f7018c21STomi Valkeinen (id == CYBERBLADEAi1) || 119f7018c21STomi Valkeinen (id == CYBERBLADEAi1D); 120f7018c21STomi Valkeinen } 121f7018c21STomi Valkeinen 122f7018c21STomi Valkeinen static inline int is_xp(int id) 123f7018c21STomi Valkeinen { 124f7018c21STomi Valkeinen return (id == CYBERBLADEXPAi1) || 125f7018c21STomi Valkeinen (id == CYBERBLADEXPm8) || 126f7018c21STomi Valkeinen (id == CYBERBLADEXPm16); 127f7018c21STomi Valkeinen } 128f7018c21STomi Valkeinen 129f7018c21STomi Valkeinen static inline int is3Dchip(int id) 130f7018c21STomi Valkeinen { 131f7018c21STomi Valkeinen return is_blade(id) || is_xp(id) || 132f7018c21STomi Valkeinen (id == CYBER9397) || (id == CYBER9397DVD) || 133f7018c21STomi Valkeinen (id == CYBER9520) || (id == CYBER9525DVD) || 134f7018c21STomi Valkeinen (id == IMAGE975) || (id == IMAGE985); 135f7018c21STomi Valkeinen } 136f7018c21STomi Valkeinen 137f7018c21STomi Valkeinen static inline int iscyber(int id) 138f7018c21STomi Valkeinen { 139f7018c21STomi Valkeinen switch (id) { 140f7018c21STomi Valkeinen case CYBER9388: 141f7018c21STomi Valkeinen case CYBER9382: 142f7018c21STomi Valkeinen case CYBER9385: 143f7018c21STomi Valkeinen case CYBER9397: 144f7018c21STomi Valkeinen case CYBER9397DVD: 145f7018c21STomi Valkeinen case CYBER9520: 146f7018c21STomi Valkeinen case CYBER9525DVD: 147f7018c21STomi Valkeinen case CYBERBLADEE4: 148f7018c21STomi Valkeinen case CYBERBLADEi7D: 149f7018c21STomi Valkeinen case CYBERBLADEi1: 150f7018c21STomi Valkeinen case CYBERBLADEi1D: 151f7018c21STomi Valkeinen case CYBERBLADEAi1: 152f7018c21STomi Valkeinen case CYBERBLADEAi1D: 153f7018c21STomi Valkeinen case CYBERBLADEXPAi1: 154f7018c21STomi Valkeinen return 1; 155f7018c21STomi Valkeinen 156f7018c21STomi Valkeinen case CYBER9320: 157f7018c21STomi Valkeinen case CYBERBLADEi7: /* VIA MPV4 integrated version */ 158f7018c21STomi Valkeinen default: 159f7018c21STomi Valkeinen /* case CYBERBLDAEXPm8: Strange */ 160f7018c21STomi Valkeinen /* case CYBERBLDAEXPm16: Strange */ 161f7018c21STomi Valkeinen return 0; 162f7018c21STomi Valkeinen } 163f7018c21STomi Valkeinen } 164f7018c21STomi Valkeinen 165f7018c21STomi Valkeinen static inline void t_outb(struct tridentfb_par *p, u8 val, u16 reg) 166f7018c21STomi Valkeinen { 167f7018c21STomi Valkeinen fb_writeb(val, p->io_virt + reg); 168f7018c21STomi Valkeinen } 169f7018c21STomi Valkeinen 170f7018c21STomi Valkeinen static inline u8 t_inb(struct tridentfb_par *p, u16 reg) 171f7018c21STomi Valkeinen { 172f7018c21STomi Valkeinen return fb_readb(p->io_virt + reg); 173f7018c21STomi Valkeinen } 174f7018c21STomi Valkeinen 175f7018c21STomi Valkeinen static inline void writemmr(struct tridentfb_par *par, u16 r, u32 v) 176f7018c21STomi Valkeinen { 177f7018c21STomi Valkeinen fb_writel(v, par->io_virt + r); 178f7018c21STomi Valkeinen } 179f7018c21STomi Valkeinen 180f7018c21STomi Valkeinen static inline u32 readmmr(struct tridentfb_par *par, u16 r) 181f7018c21STomi Valkeinen { 182f7018c21STomi Valkeinen return fb_readl(par->io_virt + r); 183f7018c21STomi Valkeinen } 184f7018c21STomi Valkeinen 1856a5e3bd0SOndrej Zary #define DDC_SDA_TGUI BIT(0) 1866a5e3bd0SOndrej Zary #define DDC_SCL_TGUI BIT(1) 1876a5e3bd0SOndrej Zary #define DDC_SCL_DRIVE_TGUI BIT(2) 1886a5e3bd0SOndrej Zary #define DDC_SDA_DRIVE_TGUI BIT(3) 1896a5e3bd0SOndrej Zary #define DDC_MASK_TGUI (DDC_SCL_DRIVE_TGUI | DDC_SDA_DRIVE_TGUI) 1906a5e3bd0SOndrej Zary 1916a5e3bd0SOndrej Zary static void tridentfb_ddc_setscl_tgui(void *data, int val) 1926a5e3bd0SOndrej Zary { 1936a5e3bd0SOndrej Zary struct tridentfb_par *par = data; 1946a5e3bd0SOndrej Zary u8 reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK_TGUI; 1956a5e3bd0SOndrej Zary 1966a5e3bd0SOndrej Zary if (val) 1976a5e3bd0SOndrej Zary reg &= ~DDC_SCL_DRIVE_TGUI; /* disable drive - don't drive hi */ 1986a5e3bd0SOndrej Zary else 1996a5e3bd0SOndrej Zary reg |= DDC_SCL_DRIVE_TGUI; /* drive low */ 2006a5e3bd0SOndrej Zary 2016a5e3bd0SOndrej Zary vga_mm_wcrt(par->io_virt, I2C, reg); 2026a5e3bd0SOndrej Zary } 2036a5e3bd0SOndrej Zary 2046a5e3bd0SOndrej Zary static void tridentfb_ddc_setsda_tgui(void *data, int val) 2056a5e3bd0SOndrej Zary { 2066a5e3bd0SOndrej Zary struct tridentfb_par *par = data; 2076a5e3bd0SOndrej Zary u8 reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK_TGUI; 2086a5e3bd0SOndrej Zary 2096a5e3bd0SOndrej Zary if (val) 2106a5e3bd0SOndrej Zary reg &= ~DDC_SDA_DRIVE_TGUI; /* disable drive - don't drive hi */ 2116a5e3bd0SOndrej Zary else 2126a5e3bd0SOndrej Zary reg |= DDC_SDA_DRIVE_TGUI; /* drive low */ 2136a5e3bd0SOndrej Zary 2146a5e3bd0SOndrej Zary vga_mm_wcrt(par->io_virt, I2C, reg); 2156a5e3bd0SOndrej Zary } 2166a5e3bd0SOndrej Zary 2176a5e3bd0SOndrej Zary static int tridentfb_ddc_getsda_tgui(void *data) 2186a5e3bd0SOndrej Zary { 2196a5e3bd0SOndrej Zary struct tridentfb_par *par = data; 2206a5e3bd0SOndrej Zary 2216a5e3bd0SOndrej Zary return !!(vga_mm_rcrt(par->io_virt, I2C) & DDC_SDA_TGUI); 2226a5e3bd0SOndrej Zary } 2236a5e3bd0SOndrej Zary 2246a5e3bd0SOndrej Zary #define DDC_SDA_IN BIT(0) 2256a5e3bd0SOndrej Zary #define DDC_SCL_OUT BIT(1) 2266a5e3bd0SOndrej Zary #define DDC_SDA_OUT BIT(3) 2276a5e3bd0SOndrej Zary #define DDC_SCL_IN BIT(6) 2286a5e3bd0SOndrej Zary #define DDC_MASK (DDC_SCL_OUT | DDC_SDA_OUT) 2296a5e3bd0SOndrej Zary 2306a5e3bd0SOndrej Zary static void tridentfb_ddc_setscl(void *data, int val) 2316a5e3bd0SOndrej Zary { 2326a5e3bd0SOndrej Zary struct tridentfb_par *par = data; 2336a5e3bd0SOndrej Zary unsigned char reg; 2346a5e3bd0SOndrej Zary 2356a5e3bd0SOndrej Zary reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK; 2366a5e3bd0SOndrej Zary if (val) 2376a5e3bd0SOndrej Zary reg |= DDC_SCL_OUT; 2386a5e3bd0SOndrej Zary else 2396a5e3bd0SOndrej Zary reg &= ~DDC_SCL_OUT; 2406a5e3bd0SOndrej Zary vga_mm_wcrt(par->io_virt, I2C, reg); 2416a5e3bd0SOndrej Zary } 2426a5e3bd0SOndrej Zary 2436a5e3bd0SOndrej Zary static void tridentfb_ddc_setsda(void *data, int val) 2446a5e3bd0SOndrej Zary { 2456a5e3bd0SOndrej Zary struct tridentfb_par *par = data; 2466a5e3bd0SOndrej Zary unsigned char reg; 2476a5e3bd0SOndrej Zary 2486a5e3bd0SOndrej Zary reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK; 2496a5e3bd0SOndrej Zary if (!val) 2506a5e3bd0SOndrej Zary reg |= DDC_SDA_OUT; 2516a5e3bd0SOndrej Zary else 2526a5e3bd0SOndrej Zary reg &= ~DDC_SDA_OUT; 2536a5e3bd0SOndrej Zary vga_mm_wcrt(par->io_virt, I2C, reg); 2546a5e3bd0SOndrej Zary } 2556a5e3bd0SOndrej Zary 2566a5e3bd0SOndrej Zary static int tridentfb_ddc_getscl(void *data) 2576a5e3bd0SOndrej Zary { 2586a5e3bd0SOndrej Zary struct tridentfb_par *par = data; 2596a5e3bd0SOndrej Zary 2606a5e3bd0SOndrej Zary return !!(vga_mm_rcrt(par->io_virt, I2C) & DDC_SCL_IN); 2616a5e3bd0SOndrej Zary } 2626a5e3bd0SOndrej Zary 2636a5e3bd0SOndrej Zary static int tridentfb_ddc_getsda(void *data) 2646a5e3bd0SOndrej Zary { 2656a5e3bd0SOndrej Zary struct tridentfb_par *par = data; 2666a5e3bd0SOndrej Zary 2676a5e3bd0SOndrej Zary return !!(vga_mm_rcrt(par->io_virt, I2C) & DDC_SDA_IN); 2686a5e3bd0SOndrej Zary } 2696a5e3bd0SOndrej Zary 2706a5e3bd0SOndrej Zary static int tridentfb_setup_ddc_bus(struct fb_info *info) 2716a5e3bd0SOndrej Zary { 2726a5e3bd0SOndrej Zary struct tridentfb_par *par = info->par; 2736a5e3bd0SOndrej Zary 2746a5e3bd0SOndrej Zary strlcpy(par->ddc_adapter.name, info->fix.id, 2756a5e3bd0SOndrej Zary sizeof(par->ddc_adapter.name)); 2766a5e3bd0SOndrej Zary par->ddc_adapter.owner = THIS_MODULE; 2776a5e3bd0SOndrej Zary par->ddc_adapter.class = I2C_CLASS_DDC; 2786a5e3bd0SOndrej Zary par->ddc_adapter.algo_data = &par->ddc_algo; 2796a5e3bd0SOndrej Zary par->ddc_adapter.dev.parent = info->device; 2806a5e3bd0SOndrej Zary if (is_oldclock(par->chip_id)) { /* not sure if this check is OK */ 2816a5e3bd0SOndrej Zary par->ddc_algo.setsda = tridentfb_ddc_setsda_tgui; 2826a5e3bd0SOndrej Zary par->ddc_algo.setscl = tridentfb_ddc_setscl_tgui; 2836a5e3bd0SOndrej Zary par->ddc_algo.getsda = tridentfb_ddc_getsda_tgui; 2846a5e3bd0SOndrej Zary /* no getscl */ 2856a5e3bd0SOndrej Zary } else { 2866a5e3bd0SOndrej Zary par->ddc_algo.setsda = tridentfb_ddc_setsda; 2876a5e3bd0SOndrej Zary par->ddc_algo.setscl = tridentfb_ddc_setscl; 2886a5e3bd0SOndrej Zary par->ddc_algo.getsda = tridentfb_ddc_getsda; 2896a5e3bd0SOndrej Zary par->ddc_algo.getscl = tridentfb_ddc_getscl; 2906a5e3bd0SOndrej Zary } 2916a5e3bd0SOndrej Zary par->ddc_algo.udelay = 10; 2926a5e3bd0SOndrej Zary par->ddc_algo.timeout = 20; 2936a5e3bd0SOndrej Zary par->ddc_algo.data = par; 2946a5e3bd0SOndrej Zary 2956a5e3bd0SOndrej Zary i2c_set_adapdata(&par->ddc_adapter, par); 2966a5e3bd0SOndrej Zary 2976a5e3bd0SOndrej Zary return i2c_bit_add_bus(&par->ddc_adapter); 2986a5e3bd0SOndrej Zary } 2996a5e3bd0SOndrej Zary 300f7018c21STomi Valkeinen /* 301f7018c21STomi Valkeinen * Blade specific acceleration. 302f7018c21STomi Valkeinen */ 303f7018c21STomi Valkeinen 304f7018c21STomi Valkeinen #define point(x, y) ((y) << 16 | (x)) 305f7018c21STomi Valkeinen 306f7018c21STomi Valkeinen static void blade_init_accel(struct tridentfb_par *par, int pitch, int bpp) 307f7018c21STomi Valkeinen { 308f7018c21STomi Valkeinen int v1 = (pitch >> 3) << 20; 309f7018c21STomi Valkeinen int tmp = bpp == 24 ? 2 : (bpp >> 4); 310f7018c21STomi Valkeinen int v2 = v1 | (tmp << 29); 311f7018c21STomi Valkeinen 312f7018c21STomi Valkeinen writemmr(par, 0x21C0, v2); 313f7018c21STomi Valkeinen writemmr(par, 0x21C4, v2); 314f7018c21STomi Valkeinen writemmr(par, 0x21B8, v2); 315f7018c21STomi Valkeinen writemmr(par, 0x21BC, v2); 316f7018c21STomi Valkeinen writemmr(par, 0x21D0, v1); 317f7018c21STomi Valkeinen writemmr(par, 0x21D4, v1); 318f7018c21STomi Valkeinen writemmr(par, 0x21C8, v1); 319f7018c21STomi Valkeinen writemmr(par, 0x21CC, v1); 320f7018c21STomi Valkeinen writemmr(par, 0x216C, 0); 321f7018c21STomi Valkeinen } 322f7018c21STomi Valkeinen 323f7018c21STomi Valkeinen static void blade_wait_engine(struct tridentfb_par *par) 324f7018c21STomi Valkeinen { 325f7018c21STomi Valkeinen while (readmmr(par, STATUS) & 0xFA800000) 326f7018c21STomi Valkeinen cpu_relax(); 327f7018c21STomi Valkeinen } 328f7018c21STomi Valkeinen 329f7018c21STomi Valkeinen static void blade_fill_rect(struct tridentfb_par *par, 330f7018c21STomi Valkeinen u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop) 331f7018c21STomi Valkeinen { 332f7018c21STomi Valkeinen writemmr(par, COLOR, c); 333f7018c21STomi Valkeinen writemmr(par, ROP, rop ? ROP_X : ROP_S); 334f7018c21STomi Valkeinen writemmr(par, CMD, 0x20000000 | 1 << 19 | 1 << 4 | 2 << 2); 335f7018c21STomi Valkeinen 336f7018c21STomi Valkeinen writemmr(par, DST1, point(x, y)); 337f7018c21STomi Valkeinen writemmr(par, DST2, point(x + w - 1, y + h - 1)); 338f7018c21STomi Valkeinen } 339f7018c21STomi Valkeinen 340f7018c21STomi Valkeinen static void blade_image_blit(struct tridentfb_par *par, const char *data, 341f7018c21STomi Valkeinen u32 x, u32 y, u32 w, u32 h, u32 c, u32 b) 342f7018c21STomi Valkeinen { 343f7018c21STomi Valkeinen unsigned size = ((w + 31) >> 5) * h; 344f7018c21STomi Valkeinen 345f7018c21STomi Valkeinen writemmr(par, COLOR, c); 346f7018c21STomi Valkeinen writemmr(par, BGCOLOR, b); 347f7018c21STomi Valkeinen writemmr(par, CMD, 0xa0000000 | 3 << 19); 348f7018c21STomi Valkeinen 349f7018c21STomi Valkeinen writemmr(par, DST1, point(x, y)); 350f7018c21STomi Valkeinen writemmr(par, DST2, point(x + w - 1, y + h - 1)); 351f7018c21STomi Valkeinen 3527f2ea957SOndrej Zary iowrite32_rep(par->io_virt + 0x10000, data, size); 353f7018c21STomi Valkeinen } 354f7018c21STomi Valkeinen 355f7018c21STomi Valkeinen static void blade_copy_rect(struct tridentfb_par *par, 356f7018c21STomi Valkeinen u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h) 357f7018c21STomi Valkeinen { 358f7018c21STomi Valkeinen int direction = 2; 359f7018c21STomi Valkeinen u32 s1 = point(x1, y1); 360f7018c21STomi Valkeinen u32 s2 = point(x1 + w - 1, y1 + h - 1); 361f7018c21STomi Valkeinen u32 d1 = point(x2, y2); 362f7018c21STomi Valkeinen u32 d2 = point(x2 + w - 1, y2 + h - 1); 363f7018c21STomi Valkeinen 364f7018c21STomi Valkeinen if ((y1 > y2) || ((y1 == y2) && (x1 > x2))) 365f7018c21STomi Valkeinen direction = 0; 366f7018c21STomi Valkeinen 367f7018c21STomi Valkeinen writemmr(par, ROP, ROP_S); 368f7018c21STomi Valkeinen writemmr(par, CMD, 0xE0000000 | 1 << 19 | 1 << 4 | 1 << 2 | direction); 369f7018c21STomi Valkeinen 370f7018c21STomi Valkeinen writemmr(par, SRC1, direction ? s2 : s1); 371f7018c21STomi Valkeinen writemmr(par, SRC2, direction ? s1 : s2); 372f7018c21STomi Valkeinen writemmr(par, DST1, direction ? d2 : d1); 373f7018c21STomi Valkeinen writemmr(par, DST2, direction ? d1 : d2); 374f7018c21STomi Valkeinen } 375f7018c21STomi Valkeinen 376f7018c21STomi Valkeinen /* 377f7018c21STomi Valkeinen * BladeXP specific acceleration functions 378f7018c21STomi Valkeinen */ 379f7018c21STomi Valkeinen 380f7018c21STomi Valkeinen static void xp_init_accel(struct tridentfb_par *par, int pitch, int bpp) 381f7018c21STomi Valkeinen { 382f7018c21STomi Valkeinen unsigned char x = bpp == 24 ? 3 : (bpp >> 4); 383f7018c21STomi Valkeinen int v1 = pitch << (bpp == 24 ? 20 : (18 + x)); 384f7018c21STomi Valkeinen 385f7018c21STomi Valkeinen switch (pitch << (bpp >> 3)) { 386f7018c21STomi Valkeinen case 8192: 387f7018c21STomi Valkeinen case 512: 388f7018c21STomi Valkeinen x |= 0x00; 389f7018c21STomi Valkeinen break; 390f7018c21STomi Valkeinen case 1024: 391f7018c21STomi Valkeinen x |= 0x04; 392f7018c21STomi Valkeinen break; 393f7018c21STomi Valkeinen case 2048: 394f7018c21STomi Valkeinen x |= 0x08; 395f7018c21STomi Valkeinen break; 396f7018c21STomi Valkeinen case 4096: 397f7018c21STomi Valkeinen x |= 0x0C; 398f7018c21STomi Valkeinen break; 399f7018c21STomi Valkeinen } 400f7018c21STomi Valkeinen 401f7018c21STomi Valkeinen t_outb(par, x, 0x2125); 402f7018c21STomi Valkeinen 403f7018c21STomi Valkeinen par->eng_oper = x | 0x40; 404f7018c21STomi Valkeinen 405f7018c21STomi Valkeinen writemmr(par, 0x2154, v1); 406f7018c21STomi Valkeinen writemmr(par, 0x2150, v1); 407f7018c21STomi Valkeinen t_outb(par, 3, 0x2126); 408f7018c21STomi Valkeinen } 409f7018c21STomi Valkeinen 410f7018c21STomi Valkeinen static void xp_wait_engine(struct tridentfb_par *par) 411f7018c21STomi Valkeinen { 412f7018c21STomi Valkeinen int count = 0; 413f7018c21STomi Valkeinen int timeout = 0; 414f7018c21STomi Valkeinen 415f7018c21STomi Valkeinen while (t_inb(par, STATUS) & 0x80) { 416f7018c21STomi Valkeinen count++; 417f7018c21STomi Valkeinen if (count == 10000000) { 418f7018c21STomi Valkeinen /* Timeout */ 419f7018c21STomi Valkeinen count = 9990000; 420f7018c21STomi Valkeinen timeout++; 421f7018c21STomi Valkeinen if (timeout == 8) { 422f7018c21STomi Valkeinen /* Reset engine */ 423f7018c21STomi Valkeinen t_outb(par, 0x00, STATUS); 424f7018c21STomi Valkeinen return; 425f7018c21STomi Valkeinen } 426f7018c21STomi Valkeinen } 427f7018c21STomi Valkeinen cpu_relax(); 428f7018c21STomi Valkeinen } 429f7018c21STomi Valkeinen } 430f7018c21STomi Valkeinen 431f7018c21STomi Valkeinen static void xp_fill_rect(struct tridentfb_par *par, 432f7018c21STomi Valkeinen u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop) 433f7018c21STomi Valkeinen { 434f7018c21STomi Valkeinen writemmr(par, 0x2127, ROP_P); 435f7018c21STomi Valkeinen writemmr(par, 0x2158, c); 436f7018c21STomi Valkeinen writemmr(par, DRAWFL, 0x4000); 437f7018c21STomi Valkeinen writemmr(par, OLDDIM, point(h, w)); 438f7018c21STomi Valkeinen writemmr(par, OLDDST, point(y, x)); 439f7018c21STomi Valkeinen t_outb(par, 0x01, OLDCMD); 440f7018c21STomi Valkeinen t_outb(par, par->eng_oper, 0x2125); 441f7018c21STomi Valkeinen } 442f7018c21STomi Valkeinen 443f7018c21STomi Valkeinen static void xp_copy_rect(struct tridentfb_par *par, 444f7018c21STomi Valkeinen u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h) 445f7018c21STomi Valkeinen { 446f7018c21STomi Valkeinen u32 x1_tmp, x2_tmp, y1_tmp, y2_tmp; 447f7018c21STomi Valkeinen int direction = 0x0004; 448f7018c21STomi Valkeinen 449f7018c21STomi Valkeinen if ((x1 < x2) && (y1 == y2)) { 450f7018c21STomi Valkeinen direction |= 0x0200; 451f7018c21STomi Valkeinen x1_tmp = x1 + w - 1; 452f7018c21STomi Valkeinen x2_tmp = x2 + w - 1; 453f7018c21STomi Valkeinen } else { 454f7018c21STomi Valkeinen x1_tmp = x1; 455f7018c21STomi Valkeinen x2_tmp = x2; 456f7018c21STomi Valkeinen } 457f7018c21STomi Valkeinen 458f7018c21STomi Valkeinen if (y1 < y2) { 459f7018c21STomi Valkeinen direction |= 0x0100; 460f7018c21STomi Valkeinen y1_tmp = y1 + h - 1; 461f7018c21STomi Valkeinen y2_tmp = y2 + h - 1; 462f7018c21STomi Valkeinen } else { 463f7018c21STomi Valkeinen y1_tmp = y1; 464f7018c21STomi Valkeinen y2_tmp = y2; 465f7018c21STomi Valkeinen } 466f7018c21STomi Valkeinen 467f7018c21STomi Valkeinen writemmr(par, DRAWFL, direction); 468f7018c21STomi Valkeinen t_outb(par, ROP_S, 0x2127); 469f7018c21STomi Valkeinen writemmr(par, OLDSRC, point(y1_tmp, x1_tmp)); 470f7018c21STomi Valkeinen writemmr(par, OLDDST, point(y2_tmp, x2_tmp)); 471f7018c21STomi Valkeinen writemmr(par, OLDDIM, point(h, w)); 472f7018c21STomi Valkeinen t_outb(par, 0x01, OLDCMD); 473f7018c21STomi Valkeinen } 474f7018c21STomi Valkeinen 475f7018c21STomi Valkeinen /* 476f7018c21STomi Valkeinen * Image specific acceleration functions 477f7018c21STomi Valkeinen */ 478f7018c21STomi Valkeinen static void image_init_accel(struct tridentfb_par *par, int pitch, int bpp) 479f7018c21STomi Valkeinen { 480f7018c21STomi Valkeinen int tmp = bpp == 24 ? 2: (bpp >> 4); 481f7018c21STomi Valkeinen 482f7018c21STomi Valkeinen writemmr(par, 0x2120, 0xF0000000); 483f7018c21STomi Valkeinen writemmr(par, 0x2120, 0x40000000 | tmp); 484f7018c21STomi Valkeinen writemmr(par, 0x2120, 0x80000000); 485f7018c21STomi Valkeinen writemmr(par, 0x2144, 0x00000000); 486f7018c21STomi Valkeinen writemmr(par, 0x2148, 0x00000000); 487f7018c21STomi Valkeinen writemmr(par, 0x2150, 0x00000000); 488f7018c21STomi Valkeinen writemmr(par, 0x2154, 0x00000000); 489f7018c21STomi Valkeinen writemmr(par, 0x2120, 0x60000000 | (pitch << 16) | pitch); 490f7018c21STomi Valkeinen writemmr(par, 0x216C, 0x00000000); 491f7018c21STomi Valkeinen writemmr(par, 0x2170, 0x00000000); 492f7018c21STomi Valkeinen writemmr(par, 0x217C, 0x00000000); 493f7018c21STomi Valkeinen writemmr(par, 0x2120, 0x10000000); 494f7018c21STomi Valkeinen writemmr(par, 0x2130, (2047 << 16) | 2047); 495f7018c21STomi Valkeinen } 496f7018c21STomi Valkeinen 497f7018c21STomi Valkeinen static void image_wait_engine(struct tridentfb_par *par) 498f7018c21STomi Valkeinen { 499f7018c21STomi Valkeinen while (readmmr(par, 0x2164) & 0xF0000000) 500f7018c21STomi Valkeinen cpu_relax(); 501f7018c21STomi Valkeinen } 502f7018c21STomi Valkeinen 503f7018c21STomi Valkeinen static void image_fill_rect(struct tridentfb_par *par, 504f7018c21STomi Valkeinen u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop) 505f7018c21STomi Valkeinen { 506f7018c21STomi Valkeinen writemmr(par, 0x2120, 0x80000000); 507f7018c21STomi Valkeinen writemmr(par, 0x2120, 0x90000000 | ROP_S); 508f7018c21STomi Valkeinen 509f7018c21STomi Valkeinen writemmr(par, 0x2144, c); 510f7018c21STomi Valkeinen 511f7018c21STomi Valkeinen writemmr(par, DST1, point(x, y)); 512f7018c21STomi Valkeinen writemmr(par, DST2, point(x + w - 1, y + h - 1)); 513f7018c21STomi Valkeinen 514f7018c21STomi Valkeinen writemmr(par, 0x2124, 0x80000000 | 3 << 22 | 1 << 10 | 1 << 9); 515f7018c21STomi Valkeinen } 516f7018c21STomi Valkeinen 517f7018c21STomi Valkeinen static void image_copy_rect(struct tridentfb_par *par, 518f7018c21STomi Valkeinen u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h) 519f7018c21STomi Valkeinen { 520f7018c21STomi Valkeinen int direction = 0x4; 521f7018c21STomi Valkeinen u32 s1 = point(x1, y1); 522f7018c21STomi Valkeinen u32 s2 = point(x1 + w - 1, y1 + h - 1); 523f7018c21STomi Valkeinen u32 d1 = point(x2, y2); 524f7018c21STomi Valkeinen u32 d2 = point(x2 + w - 1, y2 + h - 1); 525f7018c21STomi Valkeinen 526f7018c21STomi Valkeinen if ((y1 > y2) || ((y1 == y2) && (x1 > x2))) 527f7018c21STomi Valkeinen direction = 0; 528f7018c21STomi Valkeinen 529f7018c21STomi Valkeinen writemmr(par, 0x2120, 0x80000000); 530f7018c21STomi Valkeinen writemmr(par, 0x2120, 0x90000000 | ROP_S); 531f7018c21STomi Valkeinen 532f7018c21STomi Valkeinen writemmr(par, SRC1, direction ? s2 : s1); 533f7018c21STomi Valkeinen writemmr(par, SRC2, direction ? s1 : s2); 534f7018c21STomi Valkeinen writemmr(par, DST1, direction ? d2 : d1); 535f7018c21STomi Valkeinen writemmr(par, DST2, direction ? d1 : d2); 536f7018c21STomi Valkeinen writemmr(par, 0x2124, 537f7018c21STomi Valkeinen 0x80000000 | 1 << 22 | 1 << 10 | 1 << 7 | direction); 538f7018c21STomi Valkeinen } 539f7018c21STomi Valkeinen 540f7018c21STomi Valkeinen /* 541f7018c21STomi Valkeinen * TGUI 9440/96XX acceleration 542f7018c21STomi Valkeinen */ 543f7018c21STomi Valkeinen 544f7018c21STomi Valkeinen static void tgui_init_accel(struct tridentfb_par *par, int pitch, int bpp) 545f7018c21STomi Valkeinen { 546f7018c21STomi Valkeinen unsigned char x = bpp == 24 ? 3 : (bpp >> 4); 547f7018c21STomi Valkeinen 548f7018c21STomi Valkeinen /* disable clipping */ 549f7018c21STomi Valkeinen writemmr(par, 0x2148, 0); 550f7018c21STomi Valkeinen writemmr(par, 0x214C, point(4095, 2047)); 551f7018c21STomi Valkeinen 552f7018c21STomi Valkeinen switch ((pitch * bpp) / 8) { 553f7018c21STomi Valkeinen case 8192: 554f7018c21STomi Valkeinen case 512: 555f7018c21STomi Valkeinen x |= 0x00; 556f7018c21STomi Valkeinen break; 557f7018c21STomi Valkeinen case 1024: 558f7018c21STomi Valkeinen x |= 0x04; 559f7018c21STomi Valkeinen break; 560f7018c21STomi Valkeinen case 2048: 561f7018c21STomi Valkeinen x |= 0x08; 562f7018c21STomi Valkeinen break; 563f7018c21STomi Valkeinen case 4096: 564f7018c21STomi Valkeinen x |= 0x0C; 565f7018c21STomi Valkeinen break; 566f7018c21STomi Valkeinen } 567f7018c21STomi Valkeinen 568f7018c21STomi Valkeinen fb_writew(x, par->io_virt + 0x2122); 569f7018c21STomi Valkeinen } 570f7018c21STomi Valkeinen 571f7018c21STomi Valkeinen static void tgui_fill_rect(struct tridentfb_par *par, 572f7018c21STomi Valkeinen u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop) 573f7018c21STomi Valkeinen { 574f7018c21STomi Valkeinen t_outb(par, ROP_P, 0x2127); 575f7018c21STomi Valkeinen writemmr(par, OLDCLR, c); 576f7018c21STomi Valkeinen writemmr(par, DRAWFL, 0x4020); 577f7018c21STomi Valkeinen writemmr(par, OLDDIM, point(w - 1, h - 1)); 578f7018c21STomi Valkeinen writemmr(par, OLDDST, point(x, y)); 579f7018c21STomi Valkeinen t_outb(par, 1, OLDCMD); 580f7018c21STomi Valkeinen } 581f7018c21STomi Valkeinen 582f7018c21STomi Valkeinen static void tgui_copy_rect(struct tridentfb_par *par, 583f7018c21STomi Valkeinen u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h) 584f7018c21STomi Valkeinen { 585f7018c21STomi Valkeinen int flags = 0; 586f7018c21STomi Valkeinen u16 x1_tmp, x2_tmp, y1_tmp, y2_tmp; 587f7018c21STomi Valkeinen 588f7018c21STomi Valkeinen if ((x1 < x2) && (y1 == y2)) { 589f7018c21STomi Valkeinen flags |= 0x0200; 590f7018c21STomi Valkeinen x1_tmp = x1 + w - 1; 591f7018c21STomi Valkeinen x2_tmp = x2 + w - 1; 592f7018c21STomi Valkeinen } else { 593f7018c21STomi Valkeinen x1_tmp = x1; 594f7018c21STomi Valkeinen x2_tmp = x2; 595f7018c21STomi Valkeinen } 596f7018c21STomi Valkeinen 597f7018c21STomi Valkeinen if (y1 < y2) { 598f7018c21STomi Valkeinen flags |= 0x0100; 599f7018c21STomi Valkeinen y1_tmp = y1 + h - 1; 600f7018c21STomi Valkeinen y2_tmp = y2 + h - 1; 601f7018c21STomi Valkeinen } else { 602f7018c21STomi Valkeinen y1_tmp = y1; 603f7018c21STomi Valkeinen y2_tmp = y2; 604f7018c21STomi Valkeinen } 605f7018c21STomi Valkeinen 606f7018c21STomi Valkeinen writemmr(par, DRAWFL, 0x4 | flags); 607f7018c21STomi Valkeinen t_outb(par, ROP_S, 0x2127); 608f7018c21STomi Valkeinen writemmr(par, OLDSRC, point(x1_tmp, y1_tmp)); 609f7018c21STomi Valkeinen writemmr(par, OLDDST, point(x2_tmp, y2_tmp)); 610f7018c21STomi Valkeinen writemmr(par, OLDDIM, point(w - 1, h - 1)); 611f7018c21STomi Valkeinen t_outb(par, 1, OLDCMD); 612f7018c21STomi Valkeinen } 613f7018c21STomi Valkeinen 614f7018c21STomi Valkeinen /* 615f7018c21STomi Valkeinen * Accel functions called by the upper layers 616f7018c21STomi Valkeinen */ 617f7018c21STomi Valkeinen static void tridentfb_fillrect(struct fb_info *info, 618f7018c21STomi Valkeinen const struct fb_fillrect *fr) 619f7018c21STomi Valkeinen { 620f7018c21STomi Valkeinen struct tridentfb_par *par = info->par; 621f7018c21STomi Valkeinen int col; 622f7018c21STomi Valkeinen 623f7018c21STomi Valkeinen if (info->flags & FBINFO_HWACCEL_DISABLED) { 624f7018c21STomi Valkeinen cfb_fillrect(info, fr); 625f7018c21STomi Valkeinen return; 626f7018c21STomi Valkeinen } 627f7018c21STomi Valkeinen if (info->var.bits_per_pixel == 8) { 628f7018c21STomi Valkeinen col = fr->color; 629f7018c21STomi Valkeinen col |= col << 8; 630f7018c21STomi Valkeinen col |= col << 16; 631f7018c21STomi Valkeinen } else 632f7018c21STomi Valkeinen col = ((u32 *)(info->pseudo_palette))[fr->color]; 633f7018c21STomi Valkeinen 634f7018c21STomi Valkeinen par->wait_engine(par); 635f7018c21STomi Valkeinen par->fill_rect(par, fr->dx, fr->dy, fr->width, 636f7018c21STomi Valkeinen fr->height, col, fr->rop); 637f7018c21STomi Valkeinen } 638f7018c21STomi Valkeinen 639f7018c21STomi Valkeinen static void tridentfb_imageblit(struct fb_info *info, 640f7018c21STomi Valkeinen const struct fb_image *img) 641f7018c21STomi Valkeinen { 642f7018c21STomi Valkeinen struct tridentfb_par *par = info->par; 643f7018c21STomi Valkeinen int col, bgcol; 644f7018c21STomi Valkeinen 645f7018c21STomi Valkeinen if ((info->flags & FBINFO_HWACCEL_DISABLED) || img->depth != 1) { 646f7018c21STomi Valkeinen cfb_imageblit(info, img); 647f7018c21STomi Valkeinen return; 648f7018c21STomi Valkeinen } 649f7018c21STomi Valkeinen if (info->var.bits_per_pixel == 8) { 650f7018c21STomi Valkeinen col = img->fg_color; 651f7018c21STomi Valkeinen col |= col << 8; 652f7018c21STomi Valkeinen col |= col << 16; 653f7018c21STomi Valkeinen bgcol = img->bg_color; 654f7018c21STomi Valkeinen bgcol |= bgcol << 8; 655f7018c21STomi Valkeinen bgcol |= bgcol << 16; 656f7018c21STomi Valkeinen } else { 657f7018c21STomi Valkeinen col = ((u32 *)(info->pseudo_palette))[img->fg_color]; 658f7018c21STomi Valkeinen bgcol = ((u32 *)(info->pseudo_palette))[img->bg_color]; 659f7018c21STomi Valkeinen } 660f7018c21STomi Valkeinen 661f7018c21STomi Valkeinen par->wait_engine(par); 662f7018c21STomi Valkeinen if (par->image_blit) 663f7018c21STomi Valkeinen par->image_blit(par, img->data, img->dx, img->dy, 664f7018c21STomi Valkeinen img->width, img->height, col, bgcol); 665f7018c21STomi Valkeinen else 666f7018c21STomi Valkeinen cfb_imageblit(info, img); 667f7018c21STomi Valkeinen } 668f7018c21STomi Valkeinen 669f7018c21STomi Valkeinen static void tridentfb_copyarea(struct fb_info *info, 670f7018c21STomi Valkeinen const struct fb_copyarea *ca) 671f7018c21STomi Valkeinen { 672f7018c21STomi Valkeinen struct tridentfb_par *par = info->par; 673f7018c21STomi Valkeinen 674f7018c21STomi Valkeinen if (info->flags & FBINFO_HWACCEL_DISABLED) { 675f7018c21STomi Valkeinen cfb_copyarea(info, ca); 676f7018c21STomi Valkeinen return; 677f7018c21STomi Valkeinen } 678f7018c21STomi Valkeinen par->wait_engine(par); 679f7018c21STomi Valkeinen par->copy_rect(par, ca->sx, ca->sy, ca->dx, ca->dy, 680f7018c21STomi Valkeinen ca->width, ca->height); 681f7018c21STomi Valkeinen } 682f7018c21STomi Valkeinen 683f7018c21STomi Valkeinen static int tridentfb_sync(struct fb_info *info) 684f7018c21STomi Valkeinen { 685f7018c21STomi Valkeinen struct tridentfb_par *par = info->par; 686f7018c21STomi Valkeinen 687f7018c21STomi Valkeinen if (!(info->flags & FBINFO_HWACCEL_DISABLED)) 688f7018c21STomi Valkeinen par->wait_engine(par); 689f7018c21STomi Valkeinen return 0; 690f7018c21STomi Valkeinen } 691f7018c21STomi Valkeinen 692f7018c21STomi Valkeinen /* 693f7018c21STomi Valkeinen * Hardware access functions 694f7018c21STomi Valkeinen */ 695f7018c21STomi Valkeinen 696f7018c21STomi Valkeinen static inline unsigned char read3X4(struct tridentfb_par *par, int reg) 697f7018c21STomi Valkeinen { 698f7018c21STomi Valkeinen return vga_mm_rcrt(par->io_virt, reg); 699f7018c21STomi Valkeinen } 700f7018c21STomi Valkeinen 701f7018c21STomi Valkeinen static inline void write3X4(struct tridentfb_par *par, int reg, 702f7018c21STomi Valkeinen unsigned char val) 703f7018c21STomi Valkeinen { 704f7018c21STomi Valkeinen vga_mm_wcrt(par->io_virt, reg, val); 705f7018c21STomi Valkeinen } 706f7018c21STomi Valkeinen 707f7018c21STomi Valkeinen static inline unsigned char read3CE(struct tridentfb_par *par, 708f7018c21STomi Valkeinen unsigned char reg) 709f7018c21STomi Valkeinen { 710f7018c21STomi Valkeinen return vga_mm_rgfx(par->io_virt, reg); 711f7018c21STomi Valkeinen } 712f7018c21STomi Valkeinen 713f7018c21STomi Valkeinen static inline void writeAttr(struct tridentfb_par *par, int reg, 714f7018c21STomi Valkeinen unsigned char val) 715f7018c21STomi Valkeinen { 716f7018c21STomi Valkeinen fb_readb(par->io_virt + VGA_IS1_RC); /* flip-flop to index */ 717f7018c21STomi Valkeinen vga_mm_wattr(par->io_virt, reg, val); 718f7018c21STomi Valkeinen } 719f7018c21STomi Valkeinen 720f7018c21STomi Valkeinen static inline void write3CE(struct tridentfb_par *par, int reg, 721f7018c21STomi Valkeinen unsigned char val) 722f7018c21STomi Valkeinen { 723f7018c21STomi Valkeinen vga_mm_wgfx(par->io_virt, reg, val); 724f7018c21STomi Valkeinen } 725f7018c21STomi Valkeinen 726f7018c21STomi Valkeinen static void enable_mmio(struct tridentfb_par *par) 727f7018c21STomi Valkeinen { 728f7018c21STomi Valkeinen /* Goto New Mode */ 729f7018c21STomi Valkeinen vga_io_rseq(0x0B); 730f7018c21STomi Valkeinen 731f7018c21STomi Valkeinen /* Unprotect registers */ 732f7018c21STomi Valkeinen vga_io_wseq(NewMode1, 0x80); 733f7018c21STomi Valkeinen if (!is_oldprotect(par->chip_id)) 734f7018c21STomi Valkeinen vga_io_wseq(Protection, 0x92); 735f7018c21STomi Valkeinen 736f7018c21STomi Valkeinen /* Enable MMIO */ 737f7018c21STomi Valkeinen outb(PCIReg, 0x3D4); 738f7018c21STomi Valkeinen outb(inb(0x3D5) | 0x01, 0x3D5); 739f7018c21STomi Valkeinen } 740f7018c21STomi Valkeinen 741f7018c21STomi Valkeinen static void disable_mmio(struct tridentfb_par *par) 742f7018c21STomi Valkeinen { 743f7018c21STomi Valkeinen /* Goto New Mode */ 744f7018c21STomi Valkeinen vga_mm_rseq(par->io_virt, 0x0B); 745f7018c21STomi Valkeinen 746f7018c21STomi Valkeinen /* Unprotect registers */ 747f7018c21STomi Valkeinen vga_mm_wseq(par->io_virt, NewMode1, 0x80); 748f7018c21STomi Valkeinen if (!is_oldprotect(par->chip_id)) 749f7018c21STomi Valkeinen vga_mm_wseq(par->io_virt, Protection, 0x92); 750f7018c21STomi Valkeinen 751f7018c21STomi Valkeinen /* Disable MMIO */ 752f7018c21STomi Valkeinen t_outb(par, PCIReg, 0x3D4); 753f7018c21STomi Valkeinen t_outb(par, t_inb(par, 0x3D5) & ~0x01, 0x3D5); 754f7018c21STomi Valkeinen } 755f7018c21STomi Valkeinen 756f7018c21STomi Valkeinen static inline void crtc_unlock(struct tridentfb_par *par) 757f7018c21STomi Valkeinen { 758f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_V_SYNC_END, 759f7018c21STomi Valkeinen read3X4(par, VGA_CRTC_V_SYNC_END) & 0x7F); 760f7018c21STomi Valkeinen } 761f7018c21STomi Valkeinen 762f7018c21STomi Valkeinen /* Return flat panel's maximum x resolution */ 763f7018c21STomi Valkeinen static int get_nativex(struct tridentfb_par *par) 764f7018c21STomi Valkeinen { 765f7018c21STomi Valkeinen int x, y, tmp; 766f7018c21STomi Valkeinen 767f7018c21STomi Valkeinen if (nativex) 768f7018c21STomi Valkeinen return nativex; 769f7018c21STomi Valkeinen 770f7018c21STomi Valkeinen tmp = (read3CE(par, VertStretch) >> 4) & 3; 771f7018c21STomi Valkeinen 772f7018c21STomi Valkeinen switch (tmp) { 773f7018c21STomi Valkeinen case 0: 774f7018c21STomi Valkeinen x = 1280; y = 1024; 775f7018c21STomi Valkeinen break; 776f7018c21STomi Valkeinen case 2: 777f7018c21STomi Valkeinen x = 1024; y = 768; 778f7018c21STomi Valkeinen break; 779f7018c21STomi Valkeinen case 3: 780f7018c21STomi Valkeinen x = 800; y = 600; 781f7018c21STomi Valkeinen break; 782f7018c21STomi Valkeinen case 1: 783f7018c21STomi Valkeinen default: 784f7018c21STomi Valkeinen x = 640; y = 480; 785f7018c21STomi Valkeinen break; 786f7018c21STomi Valkeinen } 787f7018c21STomi Valkeinen 788f7018c21STomi Valkeinen output("%dx%d flat panel found\n", x, y); 789f7018c21STomi Valkeinen return x; 790f7018c21STomi Valkeinen } 791f7018c21STomi Valkeinen 792f7018c21STomi Valkeinen /* Set pitch */ 793f7018c21STomi Valkeinen static inline void set_lwidth(struct tridentfb_par *par, int width) 794f7018c21STomi Valkeinen { 795f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_OFFSET, width & 0xFF); 79623aa4db7SOndrej Zary /* chips older than TGUI9660 have only 1 width bit in AddColReg */ 79723aa4db7SOndrej Zary /* touching the other one breaks I2C/DDC */ 79823aa4db7SOndrej Zary if (par->chip_id == TGUI9440 || par->chip_id == CYBER9320) 79923aa4db7SOndrej Zary write3X4(par, AddColReg, 80023aa4db7SOndrej Zary (read3X4(par, AddColReg) & 0xEF) | ((width & 0x100) >> 4)); 80123aa4db7SOndrej Zary else 802f7018c21STomi Valkeinen write3X4(par, AddColReg, 803f7018c21STomi Valkeinen (read3X4(par, AddColReg) & 0xCF) | ((width & 0x300) >> 4)); 804f7018c21STomi Valkeinen } 805f7018c21STomi Valkeinen 806f7018c21STomi Valkeinen /* For resolutions smaller than FP resolution stretch */ 807f7018c21STomi Valkeinen static void screen_stretch(struct tridentfb_par *par) 808f7018c21STomi Valkeinen { 809f7018c21STomi Valkeinen if (par->chip_id != CYBERBLADEXPAi1) 810f7018c21STomi Valkeinen write3CE(par, BiosReg, 0); 811f7018c21STomi Valkeinen else 812f7018c21STomi Valkeinen write3CE(par, BiosReg, 8); 813f7018c21STomi Valkeinen write3CE(par, VertStretch, (read3CE(par, VertStretch) & 0x7C) | 1); 814f7018c21STomi Valkeinen write3CE(par, HorStretch, (read3CE(par, HorStretch) & 0x7C) | 1); 815f7018c21STomi Valkeinen } 816f7018c21STomi Valkeinen 817f7018c21STomi Valkeinen /* For resolutions smaller than FP resolution center */ 818f7018c21STomi Valkeinen static inline void screen_center(struct tridentfb_par *par) 819f7018c21STomi Valkeinen { 820f7018c21STomi Valkeinen write3CE(par, VertStretch, (read3CE(par, VertStretch) & 0x7C) | 0x80); 821f7018c21STomi Valkeinen write3CE(par, HorStretch, (read3CE(par, HorStretch) & 0x7C) | 0x80); 822f7018c21STomi Valkeinen } 823f7018c21STomi Valkeinen 824f7018c21STomi Valkeinen /* Address of first shown pixel in display memory */ 825f7018c21STomi Valkeinen static void set_screen_start(struct tridentfb_par *par, int base) 826f7018c21STomi Valkeinen { 827f7018c21STomi Valkeinen u8 tmp; 828f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_START_LO, base & 0xFF); 829f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_START_HI, (base & 0xFF00) >> 8); 830f7018c21STomi Valkeinen tmp = read3X4(par, CRTCModuleTest) & 0xDF; 831f7018c21STomi Valkeinen write3X4(par, CRTCModuleTest, tmp | ((base & 0x10000) >> 11)); 832f7018c21STomi Valkeinen tmp = read3X4(par, CRTHiOrd) & 0xF8; 833f7018c21STomi Valkeinen write3X4(par, CRTHiOrd, tmp | ((base & 0xE0000) >> 17)); 834f7018c21STomi Valkeinen } 835f7018c21STomi Valkeinen 836f7018c21STomi Valkeinen /* Set dotclock frequency */ 837f7018c21STomi Valkeinen static void set_vclk(struct tridentfb_par *par, unsigned long freq) 838f7018c21STomi Valkeinen { 839f7018c21STomi Valkeinen int m, n, k; 840f7018c21STomi Valkeinen unsigned long fi, d, di; 841f7018c21STomi Valkeinen unsigned char best_m = 0, best_n = 0, best_k = 0; 842f7018c21STomi Valkeinen unsigned char hi, lo; 843f7018c21STomi Valkeinen unsigned char shift = !is_oldclock(par->chip_id) ? 2 : 1; 844f7018c21STomi Valkeinen 845f7018c21STomi Valkeinen d = 20000; 846f7018c21STomi Valkeinen for (k = shift; k >= 0; k--) 847f7018c21STomi Valkeinen for (m = 1; m < 32; m++) { 848f7018c21STomi Valkeinen n = ((m + 2) << shift) - 8; 849f7018c21STomi Valkeinen for (n = (n < 0 ? 0 : n); n < 122; n++) { 850f7018c21STomi Valkeinen fi = ((14318l * (n + 8)) / (m + 2)) >> k; 851f7018c21STomi Valkeinen di = abs(fi - freq); 852f7018c21STomi Valkeinen if (di < d || (di == d && k == best_k)) { 853f7018c21STomi Valkeinen d = di; 854f7018c21STomi Valkeinen best_n = n; 855f7018c21STomi Valkeinen best_m = m; 856f7018c21STomi Valkeinen best_k = k; 857f7018c21STomi Valkeinen } 858f7018c21STomi Valkeinen if (fi > freq) 859f7018c21STomi Valkeinen break; 860f7018c21STomi Valkeinen } 861f7018c21STomi Valkeinen } 862f7018c21STomi Valkeinen 863f7018c21STomi Valkeinen if (is_oldclock(par->chip_id)) { 864f7018c21STomi Valkeinen lo = best_n | (best_m << 7); 865f7018c21STomi Valkeinen hi = (best_m >> 1) | (best_k << 4); 866f7018c21STomi Valkeinen } else { 867f7018c21STomi Valkeinen lo = best_n; 868f7018c21STomi Valkeinen hi = best_m | (best_k << 6); 869f7018c21STomi Valkeinen } 870f7018c21STomi Valkeinen 871f7018c21STomi Valkeinen if (is3Dchip(par->chip_id)) { 872f7018c21STomi Valkeinen vga_mm_wseq(par->io_virt, ClockHigh, hi); 873f7018c21STomi Valkeinen vga_mm_wseq(par->io_virt, ClockLow, lo); 874f7018c21STomi Valkeinen } else { 875f7018c21STomi Valkeinen t_outb(par, lo, 0x43C8); 876f7018c21STomi Valkeinen t_outb(par, hi, 0x43C9); 877f7018c21STomi Valkeinen } 878f7018c21STomi Valkeinen debug("VCLK = %X %X\n", hi, lo); 879f7018c21STomi Valkeinen } 880f7018c21STomi Valkeinen 881f7018c21STomi Valkeinen /* Set number of lines for flat panels*/ 882f7018c21STomi Valkeinen static void set_number_of_lines(struct tridentfb_par *par, int lines) 883f7018c21STomi Valkeinen { 884f7018c21STomi Valkeinen int tmp = read3CE(par, CyberEnhance) & 0x8F; 885f7018c21STomi Valkeinen if (lines > 1024) 886f7018c21STomi Valkeinen tmp |= 0x50; 887f7018c21STomi Valkeinen else if (lines > 768) 888f7018c21STomi Valkeinen tmp |= 0x30; 889f7018c21STomi Valkeinen else if (lines > 600) 890f7018c21STomi Valkeinen tmp |= 0x20; 891f7018c21STomi Valkeinen else if (lines > 480) 892f7018c21STomi Valkeinen tmp |= 0x10; 893f7018c21STomi Valkeinen write3CE(par, CyberEnhance, tmp); 894f7018c21STomi Valkeinen } 895f7018c21STomi Valkeinen 896f7018c21STomi Valkeinen /* 897f7018c21STomi Valkeinen * If we see that FP is active we assume we have one. 898f7018c21STomi Valkeinen * Otherwise we have a CRT display. User can override. 899f7018c21STomi Valkeinen */ 900f7018c21STomi Valkeinen static int is_flatpanel(struct tridentfb_par *par) 901f7018c21STomi Valkeinen { 902f7018c21STomi Valkeinen if (fp) 903f7018c21STomi Valkeinen return 1; 904f7018c21STomi Valkeinen if (crt || !iscyber(par->chip_id)) 905f7018c21STomi Valkeinen return 0; 906f7018c21STomi Valkeinen return (read3CE(par, FPConfig) & 0x10) ? 1 : 0; 907f7018c21STomi Valkeinen } 908f7018c21STomi Valkeinen 909f7018c21STomi Valkeinen /* Try detecting the video memory size */ 910f7018c21STomi Valkeinen static unsigned int get_memsize(struct tridentfb_par *par) 911f7018c21STomi Valkeinen { 912f7018c21STomi Valkeinen unsigned char tmp, tmp2; 913f7018c21STomi Valkeinen unsigned int k; 914f7018c21STomi Valkeinen 915f7018c21STomi Valkeinen /* If memory size provided by user */ 916f7018c21STomi Valkeinen if (memsize) 917f7018c21STomi Valkeinen k = memsize * Kb; 918f7018c21STomi Valkeinen else 919f7018c21STomi Valkeinen switch (par->chip_id) { 920f7018c21STomi Valkeinen case CYBER9525DVD: 921f7018c21STomi Valkeinen k = 2560 * Kb; 922f7018c21STomi Valkeinen break; 923f7018c21STomi Valkeinen default: 924f7018c21STomi Valkeinen tmp = read3X4(par, SPR) & 0x0F; 925f7018c21STomi Valkeinen switch (tmp) { 926f7018c21STomi Valkeinen 927f7018c21STomi Valkeinen case 0x01: 928f7018c21STomi Valkeinen k = 512 * Kb; 929f7018c21STomi Valkeinen break; 930f7018c21STomi Valkeinen case 0x02: 931f7018c21STomi Valkeinen k = 6 * Mb; /* XP */ 932f7018c21STomi Valkeinen break; 933f7018c21STomi Valkeinen case 0x03: 934f7018c21STomi Valkeinen k = 1 * Mb; 935f7018c21STomi Valkeinen break; 936f7018c21STomi Valkeinen case 0x04: 937f7018c21STomi Valkeinen k = 8 * Mb; 938f7018c21STomi Valkeinen break; 939f7018c21STomi Valkeinen case 0x06: 940f7018c21STomi Valkeinen k = 10 * Mb; /* XP */ 941f7018c21STomi Valkeinen break; 942f7018c21STomi Valkeinen case 0x07: 943f7018c21STomi Valkeinen k = 2 * Mb; 944f7018c21STomi Valkeinen break; 945f7018c21STomi Valkeinen case 0x08: 946f7018c21STomi Valkeinen k = 12 * Mb; /* XP */ 947f7018c21STomi Valkeinen break; 948f7018c21STomi Valkeinen case 0x0A: 949f7018c21STomi Valkeinen k = 14 * Mb; /* XP */ 950f7018c21STomi Valkeinen break; 951f7018c21STomi Valkeinen case 0x0C: 952f7018c21STomi Valkeinen k = 16 * Mb; /* XP */ 953f7018c21STomi Valkeinen break; 954f7018c21STomi Valkeinen case 0x0E: /* XP */ 955f7018c21STomi Valkeinen 956f7018c21STomi Valkeinen tmp2 = vga_mm_rseq(par->io_virt, 0xC1); 957f7018c21STomi Valkeinen switch (tmp2) { 958f7018c21STomi Valkeinen case 0x00: 959f7018c21STomi Valkeinen k = 20 * Mb; 960f7018c21STomi Valkeinen break; 961f7018c21STomi Valkeinen case 0x01: 962f7018c21STomi Valkeinen k = 24 * Mb; 963f7018c21STomi Valkeinen break; 964f7018c21STomi Valkeinen case 0x10: 965f7018c21STomi Valkeinen k = 28 * Mb; 966f7018c21STomi Valkeinen break; 967f7018c21STomi Valkeinen case 0x11: 968f7018c21STomi Valkeinen k = 32 * Mb; 969f7018c21STomi Valkeinen break; 970f7018c21STomi Valkeinen default: 971f7018c21STomi Valkeinen k = 1 * Mb; 972f7018c21STomi Valkeinen break; 973f7018c21STomi Valkeinen } 974f7018c21STomi Valkeinen break; 975f7018c21STomi Valkeinen 976f7018c21STomi Valkeinen case 0x0F: 977f7018c21STomi Valkeinen k = 4 * Mb; 978f7018c21STomi Valkeinen break; 979f7018c21STomi Valkeinen default: 980f7018c21STomi Valkeinen k = 1 * Mb; 981f7018c21STomi Valkeinen break; 982f7018c21STomi Valkeinen } 983f7018c21STomi Valkeinen } 984f7018c21STomi Valkeinen 985f7018c21STomi Valkeinen k -= memdiff * Kb; 986f7018c21STomi Valkeinen output("framebuffer size = %d Kb\n", k / Kb); 987f7018c21STomi Valkeinen return k; 988f7018c21STomi Valkeinen } 989f7018c21STomi Valkeinen 990f7018c21STomi Valkeinen /* See if we can handle the video mode described in var */ 991f7018c21STomi Valkeinen static int tridentfb_check_var(struct fb_var_screeninfo *var, 992f7018c21STomi Valkeinen struct fb_info *info) 993f7018c21STomi Valkeinen { 994f7018c21STomi Valkeinen struct tridentfb_par *par = info->par; 995f7018c21STomi Valkeinen int bpp = var->bits_per_pixel; 996f7018c21STomi Valkeinen int line_length; 997f7018c21STomi Valkeinen int ramdac = 230000; /* 230MHz for most 3D chips */ 998f7018c21STomi Valkeinen debug("enter\n"); 999f7018c21STomi Valkeinen 100016844e58SZheyu Ma if (!var->pixclock) 100116844e58SZheyu Ma return -EINVAL; 100216844e58SZheyu Ma 1003f7018c21STomi Valkeinen /* check color depth */ 1004f7018c21STomi Valkeinen if (bpp == 24) 1005f7018c21STomi Valkeinen bpp = var->bits_per_pixel = 32; 1006f7018c21STomi Valkeinen if (bpp != 8 && bpp != 16 && bpp != 32) 1007f7018c21STomi Valkeinen return -EINVAL; 1008f7018c21STomi Valkeinen if (par->chip_id == TGUI9440 && bpp == 32) 1009f7018c21STomi Valkeinen return -EINVAL; 1010f7018c21STomi Valkeinen /* check whether resolution fits on panel and in memory */ 1011f7018c21STomi Valkeinen if (par->flatpanel && nativex && var->xres > nativex) 1012f7018c21STomi Valkeinen return -EINVAL; 1013f7018c21STomi Valkeinen /* various resolution checks */ 1014f7018c21STomi Valkeinen var->xres = (var->xres + 7) & ~0x7; 1015f7018c21STomi Valkeinen if (var->xres > var->xres_virtual) 1016f7018c21STomi Valkeinen var->xres_virtual = var->xres; 1017f7018c21STomi Valkeinen if (var->yres > var->yres_virtual) 1018f7018c21STomi Valkeinen var->yres_virtual = var->yres; 1019f7018c21STomi Valkeinen if (var->xres_virtual > 4095 || var->yres > 2048) 1020f7018c21STomi Valkeinen return -EINVAL; 1021f7018c21STomi Valkeinen /* prevent from position overflow for acceleration */ 1022f7018c21STomi Valkeinen if (var->yres_virtual > 0xffff) 1023f7018c21STomi Valkeinen return -EINVAL; 1024f7018c21STomi Valkeinen line_length = var->xres_virtual * bpp / 8; 1025f7018c21STomi Valkeinen 1026f7018c21STomi Valkeinen if (!is3Dchip(par->chip_id) && 1027f7018c21STomi Valkeinen !(info->flags & FBINFO_HWACCEL_DISABLED)) { 1028f7018c21STomi Valkeinen /* acceleration requires line length to be power of 2 */ 1029f7018c21STomi Valkeinen if (line_length <= 512) 1030f7018c21STomi Valkeinen var->xres_virtual = 512 * 8 / bpp; 1031f7018c21STomi Valkeinen else if (line_length <= 1024) 1032f7018c21STomi Valkeinen var->xres_virtual = 1024 * 8 / bpp; 1033f7018c21STomi Valkeinen else if (line_length <= 2048) 1034f7018c21STomi Valkeinen var->xres_virtual = 2048 * 8 / bpp; 1035f7018c21STomi Valkeinen else if (line_length <= 4096) 1036f7018c21STomi Valkeinen var->xres_virtual = 4096 * 8 / bpp; 1037f7018c21STomi Valkeinen else if (line_length <= 8192) 1038f7018c21STomi Valkeinen var->xres_virtual = 8192 * 8 / bpp; 1039f7018c21STomi Valkeinen else 1040f7018c21STomi Valkeinen return -EINVAL; 1041f7018c21STomi Valkeinen 1042f7018c21STomi Valkeinen line_length = var->xres_virtual * bpp / 8; 1043f7018c21STomi Valkeinen } 1044f7018c21STomi Valkeinen 1045f7018c21STomi Valkeinen /* datasheet specifies how to set panning only up to 4 MB */ 1046f7018c21STomi Valkeinen if (line_length * (var->yres_virtual - var->yres) > (4 << 20)) 1047f7018c21STomi Valkeinen var->yres_virtual = ((4 << 20) / line_length) + var->yres; 1048f7018c21STomi Valkeinen 1049f7018c21STomi Valkeinen if (line_length * var->yres_virtual > info->fix.smem_len) 1050f7018c21STomi Valkeinen return -EINVAL; 1051f7018c21STomi Valkeinen 1052f7018c21STomi Valkeinen switch (bpp) { 1053f7018c21STomi Valkeinen case 8: 1054f7018c21STomi Valkeinen var->red.offset = 0; 1055f7018c21STomi Valkeinen var->red.length = 8; 1056f7018c21STomi Valkeinen var->green = var->red; 1057f7018c21STomi Valkeinen var->blue = var->red; 1058f7018c21STomi Valkeinen break; 1059f7018c21STomi Valkeinen case 16: 1060f7018c21STomi Valkeinen var->red.offset = 11; 1061f7018c21STomi Valkeinen var->green.offset = 5; 1062f7018c21STomi Valkeinen var->blue.offset = 0; 1063f7018c21STomi Valkeinen var->red.length = 5; 1064f7018c21STomi Valkeinen var->green.length = 6; 1065f7018c21STomi Valkeinen var->blue.length = 5; 1066f7018c21STomi Valkeinen break; 1067f7018c21STomi Valkeinen case 32: 1068f7018c21STomi Valkeinen var->red.offset = 16; 1069f7018c21STomi Valkeinen var->green.offset = 8; 1070f7018c21STomi Valkeinen var->blue.offset = 0; 1071f7018c21STomi Valkeinen var->red.length = 8; 1072f7018c21STomi Valkeinen var->green.length = 8; 1073f7018c21STomi Valkeinen var->blue.length = 8; 1074f7018c21STomi Valkeinen break; 1075f7018c21STomi Valkeinen default: 1076f7018c21STomi Valkeinen return -EINVAL; 1077f7018c21STomi Valkeinen } 1078f7018c21STomi Valkeinen 1079f7018c21STomi Valkeinen if (is_xp(par->chip_id)) 1080f7018c21STomi Valkeinen ramdac = 350000; 1081f7018c21STomi Valkeinen 1082f7018c21STomi Valkeinen switch (par->chip_id) { 1083f7018c21STomi Valkeinen case TGUI9440: 1084f7018c21STomi Valkeinen ramdac = (bpp >= 16) ? 45000 : 90000; 1085f7018c21STomi Valkeinen break; 1086f7018c21STomi Valkeinen case CYBER9320: 1087f7018c21STomi Valkeinen case TGUI9660: 1088f7018c21STomi Valkeinen ramdac = 135000; 1089f7018c21STomi Valkeinen break; 1090f7018c21STomi Valkeinen case PROVIDIA9685: 1091f7018c21STomi Valkeinen case CYBER9388: 1092f7018c21STomi Valkeinen case CYBER9382: 1093f7018c21STomi Valkeinen case CYBER9385: 1094f7018c21STomi Valkeinen ramdac = 170000; 1095f7018c21STomi Valkeinen break; 1096f7018c21STomi Valkeinen } 1097f7018c21STomi Valkeinen 1098f7018c21STomi Valkeinen /* The clock is doubled for 32 bpp */ 1099f7018c21STomi Valkeinen if (bpp == 32) 1100f7018c21STomi Valkeinen ramdac /= 2; 1101f7018c21STomi Valkeinen 1102f7018c21STomi Valkeinen if (PICOS2KHZ(var->pixclock) > ramdac) 1103f7018c21STomi Valkeinen return -EINVAL; 1104f7018c21STomi Valkeinen 1105f7018c21STomi Valkeinen debug("exit\n"); 1106f7018c21STomi Valkeinen 1107f7018c21STomi Valkeinen return 0; 1108f7018c21STomi Valkeinen 1109f7018c21STomi Valkeinen } 1110f7018c21STomi Valkeinen 1111f7018c21STomi Valkeinen /* Pan the display */ 1112f7018c21STomi Valkeinen static int tridentfb_pan_display(struct fb_var_screeninfo *var, 1113f7018c21STomi Valkeinen struct fb_info *info) 1114f7018c21STomi Valkeinen { 1115f7018c21STomi Valkeinen struct tridentfb_par *par = info->par; 1116f7018c21STomi Valkeinen unsigned int offset; 1117f7018c21STomi Valkeinen 1118f7018c21STomi Valkeinen debug("enter\n"); 1119f7018c21STomi Valkeinen offset = (var->xoffset + (var->yoffset * info->var.xres_virtual)) 1120f7018c21STomi Valkeinen * info->var.bits_per_pixel / 32; 1121f7018c21STomi Valkeinen set_screen_start(par, offset); 1122f7018c21STomi Valkeinen debug("exit\n"); 1123f7018c21STomi Valkeinen return 0; 1124f7018c21STomi Valkeinen } 1125f7018c21STomi Valkeinen 1126f7018c21STomi Valkeinen static inline void shadowmode_on(struct tridentfb_par *par) 1127f7018c21STomi Valkeinen { 1128f7018c21STomi Valkeinen write3CE(par, CyberControl, read3CE(par, CyberControl) | 0x81); 1129f7018c21STomi Valkeinen } 1130f7018c21STomi Valkeinen 1131f7018c21STomi Valkeinen static inline void shadowmode_off(struct tridentfb_par *par) 1132f7018c21STomi Valkeinen { 1133f7018c21STomi Valkeinen write3CE(par, CyberControl, read3CE(par, CyberControl) & 0x7E); 1134f7018c21STomi Valkeinen } 1135f7018c21STomi Valkeinen 1136f7018c21STomi Valkeinen /* Set the hardware to the requested video mode */ 1137f7018c21STomi Valkeinen static int tridentfb_set_par(struct fb_info *info) 1138f7018c21STomi Valkeinen { 1139f7018c21STomi Valkeinen struct tridentfb_par *par = info->par; 1140f7018c21STomi Valkeinen u32 htotal, hdispend, hsyncstart, hsyncend, hblankstart, hblankend; 1141f7018c21STomi Valkeinen u32 vtotal, vdispend, vsyncstart, vsyncend, vblankstart, vblankend; 1142f7018c21STomi Valkeinen struct fb_var_screeninfo *var = &info->var; 1143f7018c21STomi Valkeinen int bpp = var->bits_per_pixel; 1144f7018c21STomi Valkeinen unsigned char tmp; 1145f7018c21STomi Valkeinen unsigned long vclk; 1146f7018c21STomi Valkeinen 1147f7018c21STomi Valkeinen debug("enter\n"); 1148f7018c21STomi Valkeinen hdispend = var->xres / 8 - 1; 1149f7018c21STomi Valkeinen hsyncstart = (var->xres + var->right_margin) / 8; 1150f7018c21STomi Valkeinen hsyncend = (var->xres + var->right_margin + var->hsync_len) / 8; 1151f7018c21STomi Valkeinen htotal = (var->xres + var->left_margin + var->right_margin + 1152f7018c21STomi Valkeinen var->hsync_len) / 8 - 5; 1153f7018c21STomi Valkeinen hblankstart = hdispend + 1; 1154f7018c21STomi Valkeinen hblankend = htotal + 3; 1155f7018c21STomi Valkeinen 1156f7018c21STomi Valkeinen vdispend = var->yres - 1; 1157f7018c21STomi Valkeinen vsyncstart = var->yres + var->lower_margin; 1158f7018c21STomi Valkeinen vsyncend = vsyncstart + var->vsync_len; 1159f7018c21STomi Valkeinen vtotal = var->upper_margin + vsyncend - 2; 1160f7018c21STomi Valkeinen vblankstart = vdispend + 1; 1161f7018c21STomi Valkeinen vblankend = vtotal; 1162f7018c21STomi Valkeinen 1163f7018c21STomi Valkeinen if (info->var.vmode & FB_VMODE_INTERLACED) { 1164f7018c21STomi Valkeinen vtotal /= 2; 1165f7018c21STomi Valkeinen vdispend /= 2; 1166f7018c21STomi Valkeinen vsyncstart /= 2; 1167f7018c21STomi Valkeinen vsyncend /= 2; 1168f7018c21STomi Valkeinen vblankstart /= 2; 1169f7018c21STomi Valkeinen vblankend /= 2; 1170f7018c21STomi Valkeinen } 1171f7018c21STomi Valkeinen 1172f7018c21STomi Valkeinen enable_mmio(par); 1173f7018c21STomi Valkeinen crtc_unlock(par); 1174f7018c21STomi Valkeinen write3CE(par, CyberControl, 8); 1175f7018c21STomi Valkeinen tmp = 0xEB; 1176f7018c21STomi Valkeinen if (var->sync & FB_SYNC_HOR_HIGH_ACT) 1177f7018c21STomi Valkeinen tmp &= ~0x40; 1178f7018c21STomi Valkeinen if (var->sync & FB_SYNC_VERT_HIGH_ACT) 1179f7018c21STomi Valkeinen tmp &= ~0x80; 1180f7018c21STomi Valkeinen 1181f7018c21STomi Valkeinen if (par->flatpanel && var->xres < nativex) { 1182f7018c21STomi Valkeinen /* 1183f7018c21STomi Valkeinen * on flat panels with native size larger 1184f7018c21STomi Valkeinen * than requested resolution decide whether 1185f7018c21STomi Valkeinen * we stretch or center 1186f7018c21STomi Valkeinen */ 1187f7018c21STomi Valkeinen t_outb(par, tmp | 0xC0, VGA_MIS_W); 1188f7018c21STomi Valkeinen 1189f7018c21STomi Valkeinen shadowmode_on(par); 1190f7018c21STomi Valkeinen 1191f7018c21STomi Valkeinen if (center) 1192f7018c21STomi Valkeinen screen_center(par); 1193f7018c21STomi Valkeinen else if (stretch) 1194f7018c21STomi Valkeinen screen_stretch(par); 1195f7018c21STomi Valkeinen 1196f7018c21STomi Valkeinen } else { 1197f7018c21STomi Valkeinen t_outb(par, tmp, VGA_MIS_W); 1198f7018c21STomi Valkeinen write3CE(par, CyberControl, 8); 1199f7018c21STomi Valkeinen } 1200f7018c21STomi Valkeinen 1201f7018c21STomi Valkeinen /* vertical timing values */ 1202f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_V_TOTAL, vtotal & 0xFF); 1203f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_V_DISP_END, vdispend & 0xFF); 1204f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_V_SYNC_START, vsyncstart & 0xFF); 1205f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_V_SYNC_END, (vsyncend & 0x0F)); 1206f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_V_BLANK_START, vblankstart & 0xFF); 1207f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_V_BLANK_END, vblankend & 0xFF); 1208f7018c21STomi Valkeinen 1209f7018c21STomi Valkeinen /* horizontal timing values */ 1210f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_H_TOTAL, htotal & 0xFF); 1211f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_H_DISP, hdispend & 0xFF); 1212f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_H_SYNC_START, hsyncstart & 0xFF); 1213f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_H_SYNC_END, 1214f7018c21STomi Valkeinen (hsyncend & 0x1F) | ((hblankend & 0x20) << 2)); 1215f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_H_BLANK_START, hblankstart & 0xFF); 1216f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_H_BLANK_END, hblankend & 0x1F); 1217f7018c21STomi Valkeinen 1218f7018c21STomi Valkeinen /* higher bits of vertical timing values */ 1219f7018c21STomi Valkeinen tmp = 0x10; 1220f7018c21STomi Valkeinen if (vtotal & 0x100) tmp |= 0x01; 1221f7018c21STomi Valkeinen if (vdispend & 0x100) tmp |= 0x02; 1222f7018c21STomi Valkeinen if (vsyncstart & 0x100) tmp |= 0x04; 1223f7018c21STomi Valkeinen if (vblankstart & 0x100) tmp |= 0x08; 1224f7018c21STomi Valkeinen 1225f7018c21STomi Valkeinen if (vtotal & 0x200) tmp |= 0x20; 1226f7018c21STomi Valkeinen if (vdispend & 0x200) tmp |= 0x40; 1227f7018c21STomi Valkeinen if (vsyncstart & 0x200) tmp |= 0x80; 1228f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_OVERFLOW, tmp); 1229f7018c21STomi Valkeinen 1230f7018c21STomi Valkeinen tmp = read3X4(par, CRTHiOrd) & 0x07; 1231f7018c21STomi Valkeinen tmp |= 0x08; /* line compare bit 10 */ 1232f7018c21STomi Valkeinen if (vtotal & 0x400) tmp |= 0x80; 1233f7018c21STomi Valkeinen if (vblankstart & 0x400) tmp |= 0x40; 1234f7018c21STomi Valkeinen if (vsyncstart & 0x400) tmp |= 0x20; 1235f7018c21STomi Valkeinen if (vdispend & 0x400) tmp |= 0x10; 1236f7018c21STomi Valkeinen write3X4(par, CRTHiOrd, tmp); 1237f7018c21STomi Valkeinen 1238f7018c21STomi Valkeinen tmp = (htotal >> 8) & 0x01; 1239f7018c21STomi Valkeinen tmp |= (hdispend >> 7) & 0x02; 1240f7018c21STomi Valkeinen tmp |= (hsyncstart >> 5) & 0x08; 1241f7018c21STomi Valkeinen tmp |= (hblankstart >> 4) & 0x10; 1242f7018c21STomi Valkeinen write3X4(par, HorizOverflow, tmp); 1243f7018c21STomi Valkeinen 1244f7018c21STomi Valkeinen tmp = 0x40; 1245f7018c21STomi Valkeinen if (vblankstart & 0x200) tmp |= 0x20; 1246f7018c21STomi Valkeinen //FIXME if (info->var.vmode & FB_VMODE_DOUBLE) tmp |= 0x80; /* double scan for 200 line modes */ 1247f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_MAX_SCAN, tmp); 1248f7018c21STomi Valkeinen 1249f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_LINE_COMPARE, 0xFF); 1250f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_PRESET_ROW, 0); 1251f7018c21STomi Valkeinen write3X4(par, VGA_CRTC_MODE, 0xC3); 1252f7018c21STomi Valkeinen 1253f7018c21STomi Valkeinen write3X4(par, LinearAddReg, 0x20); /* enable linear addressing */ 1254f7018c21STomi Valkeinen 1255f7018c21STomi Valkeinen tmp = (info->var.vmode & FB_VMODE_INTERLACED) ? 0x84 : 0x80; 1256f7018c21STomi Valkeinen /* enable access extended memory */ 1257f7018c21STomi Valkeinen write3X4(par, CRTCModuleTest, tmp); 1258f7018c21STomi Valkeinen tmp = read3CE(par, MiscIntContReg) & ~0x4; 1259f7018c21STomi Valkeinen if (info->var.vmode & FB_VMODE_INTERLACED) 1260f7018c21STomi Valkeinen tmp |= 0x4; 1261f7018c21STomi Valkeinen write3CE(par, MiscIntContReg, tmp); 1262f7018c21STomi Valkeinen 1263f7018c21STomi Valkeinen /* enable GE for text acceleration */ 1264f7018c21STomi Valkeinen write3X4(par, GraphEngReg, 0x80); 1265f7018c21STomi Valkeinen 1266f7018c21STomi Valkeinen switch (bpp) { 1267f7018c21STomi Valkeinen case 8: 1268f7018c21STomi Valkeinen tmp = 0x00; 1269f7018c21STomi Valkeinen break; 1270f7018c21STomi Valkeinen case 16: 1271f7018c21STomi Valkeinen tmp = 0x05; 1272f7018c21STomi Valkeinen break; 1273f7018c21STomi Valkeinen case 24: 1274f7018c21STomi Valkeinen tmp = 0x29; 1275f7018c21STomi Valkeinen break; 1276f7018c21STomi Valkeinen case 32: 1277f7018c21STomi Valkeinen tmp = 0x09; 1278f7018c21STomi Valkeinen break; 1279f7018c21STomi Valkeinen } 1280f7018c21STomi Valkeinen 1281f7018c21STomi Valkeinen write3X4(par, PixelBusReg, tmp); 1282f7018c21STomi Valkeinen 1283f7018c21STomi Valkeinen tmp = read3X4(par, DRAMControl); 1284f7018c21STomi Valkeinen if (!is_oldprotect(par->chip_id)) 1285f7018c21STomi Valkeinen tmp |= 0x10; 1286f7018c21STomi Valkeinen if (iscyber(par->chip_id)) 1287f7018c21STomi Valkeinen tmp |= 0x20; 1288f7018c21STomi Valkeinen write3X4(par, DRAMControl, tmp); /* both IO, linear enable */ 1289f7018c21STomi Valkeinen 1290f7018c21STomi Valkeinen write3X4(par, InterfaceSel, read3X4(par, InterfaceSel) | 0x40); 1291f7018c21STomi Valkeinen if (!is_xp(par->chip_id)) 1292f7018c21STomi Valkeinen write3X4(par, Performance, read3X4(par, Performance) | 0x10); 1293f7018c21STomi Valkeinen /* MMIO & PCI read and write burst enable */ 1294f7018c21STomi Valkeinen if (par->chip_id != TGUI9440 && par->chip_id != IMAGE975) 1295f7018c21STomi Valkeinen write3X4(par, PCIReg, read3X4(par, PCIReg) | 0x06); 1296f7018c21STomi Valkeinen 1297f7018c21STomi Valkeinen vga_mm_wseq(par->io_virt, 0, 3); 1298f7018c21STomi Valkeinen vga_mm_wseq(par->io_virt, 1, 1); /* set char clock 8 dots wide */ 1299f7018c21STomi Valkeinen /* enable 4 maps because needed in chain4 mode */ 1300f7018c21STomi Valkeinen vga_mm_wseq(par->io_virt, 2, 0x0F); 1301f7018c21STomi Valkeinen vga_mm_wseq(par->io_virt, 3, 0); 1302f7018c21STomi Valkeinen vga_mm_wseq(par->io_virt, 4, 0x0E); /* memory mode enable bitmaps ?? */ 1303f7018c21STomi Valkeinen 1304f7018c21STomi Valkeinen /* convert from picoseconds to kHz */ 1305f7018c21STomi Valkeinen vclk = PICOS2KHZ(info->var.pixclock); 1306f7018c21STomi Valkeinen 1307f7018c21STomi Valkeinen /* divide clock by 2 if 32bpp chain4 mode display and CPU path */ 1308f7018c21STomi Valkeinen tmp = read3CE(par, MiscExtFunc) & 0xF0; 1309f7018c21STomi Valkeinen if (bpp == 32 || (par->chip_id == TGUI9440 && bpp == 16)) { 1310f7018c21STomi Valkeinen tmp |= 8; 1311f7018c21STomi Valkeinen vclk *= 2; 1312f7018c21STomi Valkeinen } 1313f7018c21STomi Valkeinen set_vclk(par, vclk); 1314f7018c21STomi Valkeinen write3CE(par, MiscExtFunc, tmp | 0x12); 1315f7018c21STomi Valkeinen write3CE(par, 0x5, 0x40); /* no CGA compat, allow 256 col */ 1316f7018c21STomi Valkeinen write3CE(par, 0x6, 0x05); /* graphics mode */ 1317f7018c21STomi Valkeinen write3CE(par, 0x7, 0x0F); /* planes? */ 1318f7018c21STomi Valkeinen 1319f7018c21STomi Valkeinen /* graphics mode and support 256 color modes */ 1320f7018c21STomi Valkeinen writeAttr(par, 0x10, 0x41); 1321f7018c21STomi Valkeinen writeAttr(par, 0x12, 0x0F); /* planes */ 1322f7018c21STomi Valkeinen writeAttr(par, 0x13, 0); /* horizontal pel panning */ 1323f7018c21STomi Valkeinen 1324f7018c21STomi Valkeinen /* colors */ 1325f7018c21STomi Valkeinen for (tmp = 0; tmp < 0x10; tmp++) 1326f7018c21STomi Valkeinen writeAttr(par, tmp, tmp); 1327f7018c21STomi Valkeinen fb_readb(par->io_virt + VGA_IS1_RC); /* flip-flop to index */ 1328f7018c21STomi Valkeinen t_outb(par, 0x20, VGA_ATT_W); /* enable attr */ 1329f7018c21STomi Valkeinen 1330f7018c21STomi Valkeinen switch (bpp) { 1331f7018c21STomi Valkeinen case 8: 1332f7018c21STomi Valkeinen tmp = 0; 1333f7018c21STomi Valkeinen break; 1334f7018c21STomi Valkeinen case 16: 1335f7018c21STomi Valkeinen tmp = 0x30; 1336f7018c21STomi Valkeinen break; 1337f7018c21STomi Valkeinen case 24: 1338f7018c21STomi Valkeinen case 32: 1339f7018c21STomi Valkeinen tmp = 0xD0; 1340f7018c21STomi Valkeinen break; 1341f7018c21STomi Valkeinen } 1342f7018c21STomi Valkeinen 1343f7018c21STomi Valkeinen t_inb(par, VGA_PEL_IW); 1344f7018c21STomi Valkeinen t_inb(par, VGA_PEL_MSK); 1345f7018c21STomi Valkeinen t_inb(par, VGA_PEL_MSK); 1346f7018c21STomi Valkeinen t_inb(par, VGA_PEL_MSK); 1347f7018c21STomi Valkeinen t_inb(par, VGA_PEL_MSK); 1348f7018c21STomi Valkeinen t_outb(par, tmp, VGA_PEL_MSK); 1349f7018c21STomi Valkeinen t_inb(par, VGA_PEL_IW); 1350f7018c21STomi Valkeinen 1351f7018c21STomi Valkeinen if (par->flatpanel) 1352f7018c21STomi Valkeinen set_number_of_lines(par, info->var.yres); 1353f7018c21STomi Valkeinen info->fix.line_length = info->var.xres_virtual * bpp / 8; 1354f7018c21STomi Valkeinen set_lwidth(par, info->fix.line_length / 8); 1355f7018c21STomi Valkeinen 1356f7018c21STomi Valkeinen if (!(info->flags & FBINFO_HWACCEL_DISABLED)) 1357f7018c21STomi Valkeinen par->init_accel(par, info->var.xres_virtual, bpp); 1358f7018c21STomi Valkeinen 1359f7018c21STomi Valkeinen info->fix.visual = (bpp == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; 1360f7018c21STomi Valkeinen info->cmap.len = (bpp == 8) ? 256 : 16; 1361f7018c21STomi Valkeinen debug("exit\n"); 1362f7018c21STomi Valkeinen return 0; 1363f7018c21STomi Valkeinen } 1364f7018c21STomi Valkeinen 1365f7018c21STomi Valkeinen /* Set one color register */ 1366f7018c21STomi Valkeinen static int tridentfb_setcolreg(unsigned regno, unsigned red, unsigned green, 1367f7018c21STomi Valkeinen unsigned blue, unsigned transp, 1368f7018c21STomi Valkeinen struct fb_info *info) 1369f7018c21STomi Valkeinen { 1370f7018c21STomi Valkeinen int bpp = info->var.bits_per_pixel; 1371f7018c21STomi Valkeinen struct tridentfb_par *par = info->par; 1372f7018c21STomi Valkeinen 1373f7018c21STomi Valkeinen if (regno >= info->cmap.len) 1374f7018c21STomi Valkeinen return 1; 1375f7018c21STomi Valkeinen 1376f7018c21STomi Valkeinen if (bpp == 8) { 1377f7018c21STomi Valkeinen t_outb(par, 0xFF, VGA_PEL_MSK); 1378f7018c21STomi Valkeinen t_outb(par, regno, VGA_PEL_IW); 1379f7018c21STomi Valkeinen 1380f7018c21STomi Valkeinen t_outb(par, red >> 10, VGA_PEL_D); 1381f7018c21STomi Valkeinen t_outb(par, green >> 10, VGA_PEL_D); 1382f7018c21STomi Valkeinen t_outb(par, blue >> 10, VGA_PEL_D); 1383f7018c21STomi Valkeinen 1384f7018c21STomi Valkeinen } else if (regno < 16) { 1385f7018c21STomi Valkeinen if (bpp == 16) { /* RGB 565 */ 1386f7018c21STomi Valkeinen u32 col; 1387f7018c21STomi Valkeinen 1388f7018c21STomi Valkeinen col = (red & 0xF800) | ((green & 0xFC00) >> 5) | 1389f7018c21STomi Valkeinen ((blue & 0xF800) >> 11); 1390f7018c21STomi Valkeinen col |= col << 16; 1391f7018c21STomi Valkeinen ((u32 *)(info->pseudo_palette))[regno] = col; 1392f7018c21STomi Valkeinen } else if (bpp == 32) /* ARGB 8888 */ 1393f7018c21STomi Valkeinen ((u32 *)info->pseudo_palette)[regno] = 1394f7018c21STomi Valkeinen ((transp & 0xFF00) << 16) | 1395f7018c21STomi Valkeinen ((red & 0xFF00) << 8) | 1396f7018c21STomi Valkeinen ((green & 0xFF00)) | 1397f7018c21STomi Valkeinen ((blue & 0xFF00) >> 8); 1398f7018c21STomi Valkeinen } 1399f7018c21STomi Valkeinen 1400f7018c21STomi Valkeinen return 0; 1401f7018c21STomi Valkeinen } 1402f7018c21STomi Valkeinen 1403f7018c21STomi Valkeinen /* Try blanking the screen. For flat panels it does nothing */ 1404f7018c21STomi Valkeinen static int tridentfb_blank(int blank_mode, struct fb_info *info) 1405f7018c21STomi Valkeinen { 1406f7018c21STomi Valkeinen unsigned char PMCont, DPMSCont; 1407f7018c21STomi Valkeinen struct tridentfb_par *par = info->par; 1408f7018c21STomi Valkeinen 1409f7018c21STomi Valkeinen debug("enter\n"); 1410f7018c21STomi Valkeinen if (par->flatpanel) 1411f7018c21STomi Valkeinen return 0; 1412f7018c21STomi Valkeinen t_outb(par, 0x04, 0x83C8); /* Read DPMS Control */ 1413f7018c21STomi Valkeinen PMCont = t_inb(par, 0x83C6) & 0xFC; 1414f7018c21STomi Valkeinen DPMSCont = read3CE(par, PowerStatus) & 0xFC; 1415f7018c21STomi Valkeinen switch (blank_mode) { 1416f7018c21STomi Valkeinen case FB_BLANK_UNBLANK: 1417f7018c21STomi Valkeinen /* Screen: On, HSync: On, VSync: On */ 1418f7018c21STomi Valkeinen case FB_BLANK_NORMAL: 1419f7018c21STomi Valkeinen /* Screen: Off, HSync: On, VSync: On */ 1420f7018c21STomi Valkeinen PMCont |= 0x03; 1421f7018c21STomi Valkeinen DPMSCont |= 0x00; 1422f7018c21STomi Valkeinen break; 1423f7018c21STomi Valkeinen case FB_BLANK_HSYNC_SUSPEND: 1424f7018c21STomi Valkeinen /* Screen: Off, HSync: Off, VSync: On */ 1425f7018c21STomi Valkeinen PMCont |= 0x02; 1426f7018c21STomi Valkeinen DPMSCont |= 0x01; 1427f7018c21STomi Valkeinen break; 1428f7018c21STomi Valkeinen case FB_BLANK_VSYNC_SUSPEND: 1429f7018c21STomi Valkeinen /* Screen: Off, HSync: On, VSync: Off */ 1430f7018c21STomi Valkeinen PMCont |= 0x02; 1431f7018c21STomi Valkeinen DPMSCont |= 0x02; 1432f7018c21STomi Valkeinen break; 1433f7018c21STomi Valkeinen case FB_BLANK_POWERDOWN: 1434f7018c21STomi Valkeinen /* Screen: Off, HSync: Off, VSync: Off */ 1435f7018c21STomi Valkeinen PMCont |= 0x00; 1436f7018c21STomi Valkeinen DPMSCont |= 0x03; 1437f7018c21STomi Valkeinen break; 1438f7018c21STomi Valkeinen } 1439f7018c21STomi Valkeinen 1440f7018c21STomi Valkeinen write3CE(par, PowerStatus, DPMSCont); 1441f7018c21STomi Valkeinen t_outb(par, 4, 0x83C8); 1442f7018c21STomi Valkeinen t_outb(par, PMCont, 0x83C6); 1443f7018c21STomi Valkeinen 1444f7018c21STomi Valkeinen debug("exit\n"); 1445f7018c21STomi Valkeinen 1446f7018c21STomi Valkeinen /* let fbcon do a softblank for us */ 1447f7018c21STomi Valkeinen return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0; 1448f7018c21STomi Valkeinen } 1449f7018c21STomi Valkeinen 14508a48ac33SJani Nikula static const struct fb_ops tridentfb_ops = { 1451f7018c21STomi Valkeinen .owner = THIS_MODULE, 1452f7018c21STomi Valkeinen .fb_setcolreg = tridentfb_setcolreg, 1453f7018c21STomi Valkeinen .fb_pan_display = tridentfb_pan_display, 1454f7018c21STomi Valkeinen .fb_blank = tridentfb_blank, 1455f7018c21STomi Valkeinen .fb_check_var = tridentfb_check_var, 1456f7018c21STomi Valkeinen .fb_set_par = tridentfb_set_par, 1457f7018c21STomi Valkeinen .fb_fillrect = tridentfb_fillrect, 1458f7018c21STomi Valkeinen .fb_copyarea = tridentfb_copyarea, 1459f7018c21STomi Valkeinen .fb_imageblit = tridentfb_imageblit, 1460f7018c21STomi Valkeinen .fb_sync = tridentfb_sync, 1461f7018c21STomi Valkeinen }; 1462f7018c21STomi Valkeinen 1463f7018c21STomi Valkeinen static int trident_pci_probe(struct pci_dev *dev, 1464f7018c21STomi Valkeinen const struct pci_device_id *id) 1465f7018c21STomi Valkeinen { 1466f7018c21STomi Valkeinen int err; 1467f7018c21STomi Valkeinen unsigned char revision; 1468f7018c21STomi Valkeinen struct fb_info *info; 1469f7018c21STomi Valkeinen struct tridentfb_par *default_par; 1470f7018c21STomi Valkeinen int chip3D; 1471f7018c21STomi Valkeinen int chip_id; 14726a5e3bd0SOndrej Zary bool found = false; 1473f7018c21STomi Valkeinen 1474*145eed48SThomas Zimmermann err = aperture_remove_conflicting_pci_devices(dev, "tridentfb"); 1475*145eed48SThomas Zimmermann if (err) 1476*145eed48SThomas Zimmermann return err; 1477*145eed48SThomas Zimmermann 1478f7018c21STomi Valkeinen err = pci_enable_device(dev); 1479f7018c21STomi Valkeinen if (err) 1480f7018c21STomi Valkeinen return err; 1481f7018c21STomi Valkeinen 1482f7018c21STomi Valkeinen info = framebuffer_alloc(sizeof(struct tridentfb_par), &dev->dev); 1483f7018c21STomi Valkeinen if (!info) 1484f7018c21STomi Valkeinen return -ENOMEM; 1485f7018c21STomi Valkeinen default_par = info->par; 1486f7018c21STomi Valkeinen 1487f7018c21STomi Valkeinen chip_id = id->device; 1488f7018c21STomi Valkeinen 1489f7018c21STomi Valkeinen /* If PCI id is 0x9660 then further detect chip type */ 1490f7018c21STomi Valkeinen 1491f7018c21STomi Valkeinen if (chip_id == TGUI9660) { 1492f7018c21STomi Valkeinen revision = vga_io_rseq(RevisionID); 1493f7018c21STomi Valkeinen 1494f7018c21STomi Valkeinen switch (revision) { 1495f7018c21STomi Valkeinen case 0x21: 1496f7018c21STomi Valkeinen chip_id = PROVIDIA9685; 1497f7018c21STomi Valkeinen break; 1498f7018c21STomi Valkeinen case 0x22: 1499f7018c21STomi Valkeinen case 0x23: 1500f7018c21STomi Valkeinen chip_id = CYBER9397; 1501f7018c21STomi Valkeinen break; 1502f7018c21STomi Valkeinen case 0x2A: 1503f7018c21STomi Valkeinen chip_id = CYBER9397DVD; 1504f7018c21STomi Valkeinen break; 1505f7018c21STomi Valkeinen case 0x30: 1506f7018c21STomi Valkeinen case 0x33: 1507f7018c21STomi Valkeinen case 0x34: 1508f7018c21STomi Valkeinen case 0x35: 1509f7018c21STomi Valkeinen case 0x38: 1510f7018c21STomi Valkeinen case 0x3A: 1511f7018c21STomi Valkeinen case 0xB3: 1512f7018c21STomi Valkeinen chip_id = CYBER9385; 1513f7018c21STomi Valkeinen break; 1514f7018c21STomi Valkeinen case 0x40 ... 0x43: 1515f7018c21STomi Valkeinen chip_id = CYBER9382; 1516f7018c21STomi Valkeinen break; 1517f7018c21STomi Valkeinen case 0x4A: 1518f7018c21STomi Valkeinen chip_id = CYBER9388; 1519f7018c21STomi Valkeinen break; 1520f7018c21STomi Valkeinen default: 1521f7018c21STomi Valkeinen break; 1522f7018c21STomi Valkeinen } 1523f7018c21STomi Valkeinen } 1524f7018c21STomi Valkeinen 1525f7018c21STomi Valkeinen chip3D = is3Dchip(chip_id); 1526f7018c21STomi Valkeinen 1527f7018c21STomi Valkeinen if (is_xp(chip_id)) { 1528f7018c21STomi Valkeinen default_par->init_accel = xp_init_accel; 1529f7018c21STomi Valkeinen default_par->wait_engine = xp_wait_engine; 1530f7018c21STomi Valkeinen default_par->fill_rect = xp_fill_rect; 1531f7018c21STomi Valkeinen default_par->copy_rect = xp_copy_rect; 1532f7018c21STomi Valkeinen tridentfb_fix.accel = FB_ACCEL_TRIDENT_BLADEXP; 1533f7018c21STomi Valkeinen } else if (is_blade(chip_id)) { 1534f7018c21STomi Valkeinen default_par->init_accel = blade_init_accel; 1535f7018c21STomi Valkeinen default_par->wait_engine = blade_wait_engine; 1536f7018c21STomi Valkeinen default_par->fill_rect = blade_fill_rect; 1537f7018c21STomi Valkeinen default_par->copy_rect = blade_copy_rect; 1538f7018c21STomi Valkeinen default_par->image_blit = blade_image_blit; 1539f7018c21STomi Valkeinen tridentfb_fix.accel = FB_ACCEL_TRIDENT_BLADE3D; 1540f7018c21STomi Valkeinen } else if (chip3D) { /* 3DImage family left */ 1541f7018c21STomi Valkeinen default_par->init_accel = image_init_accel; 1542f7018c21STomi Valkeinen default_par->wait_engine = image_wait_engine; 1543f7018c21STomi Valkeinen default_par->fill_rect = image_fill_rect; 1544f7018c21STomi Valkeinen default_par->copy_rect = image_copy_rect; 1545f7018c21STomi Valkeinen tridentfb_fix.accel = FB_ACCEL_TRIDENT_3DIMAGE; 1546f7018c21STomi Valkeinen } else { /* TGUI 9440/96XX family */ 1547f7018c21STomi Valkeinen default_par->init_accel = tgui_init_accel; 1548f7018c21STomi Valkeinen default_par->wait_engine = xp_wait_engine; 1549f7018c21STomi Valkeinen default_par->fill_rect = tgui_fill_rect; 1550f7018c21STomi Valkeinen default_par->copy_rect = tgui_copy_rect; 1551f7018c21STomi Valkeinen tridentfb_fix.accel = FB_ACCEL_TRIDENT_TGUI; 1552f7018c21STomi Valkeinen } 1553f7018c21STomi Valkeinen 1554f7018c21STomi Valkeinen default_par->chip_id = chip_id; 1555f7018c21STomi Valkeinen 1556f7018c21STomi Valkeinen /* setup MMIO region */ 1557f7018c21STomi Valkeinen tridentfb_fix.mmio_start = pci_resource_start(dev, 1); 1558f7018c21STomi Valkeinen tridentfb_fix.mmio_len = pci_resource_len(dev, 1); 1559f7018c21STomi Valkeinen 1560f7018c21STomi Valkeinen if (!request_mem_region(tridentfb_fix.mmio_start, 1561f7018c21STomi Valkeinen tridentfb_fix.mmio_len, "tridentfb")) { 1562f7018c21STomi Valkeinen debug("request_region failed!\n"); 1563f7018c21STomi Valkeinen framebuffer_release(info); 1564f7018c21STomi Valkeinen return -1; 1565f7018c21STomi Valkeinen } 1566f7018c21STomi Valkeinen 15674bdc0d67SChristoph Hellwig default_par->io_virt = ioremap(tridentfb_fix.mmio_start, 1568f7018c21STomi Valkeinen tridentfb_fix.mmio_len); 1569f7018c21STomi Valkeinen 1570f7018c21STomi Valkeinen if (!default_par->io_virt) { 1571f7018c21STomi Valkeinen debug("ioremap failed\n"); 1572f7018c21STomi Valkeinen err = -1; 1573f7018c21STomi Valkeinen goto out_unmap1; 1574f7018c21STomi Valkeinen } 1575f7018c21STomi Valkeinen 1576f7018c21STomi Valkeinen enable_mmio(default_par); 1577f7018c21STomi Valkeinen 1578f7018c21STomi Valkeinen /* setup framebuffer memory */ 1579f7018c21STomi Valkeinen tridentfb_fix.smem_start = pci_resource_start(dev, 0); 1580f7018c21STomi Valkeinen tridentfb_fix.smem_len = get_memsize(default_par); 1581f7018c21STomi Valkeinen 1582f7018c21STomi Valkeinen if (!request_mem_region(tridentfb_fix.smem_start, 1583f7018c21STomi Valkeinen tridentfb_fix.smem_len, "tridentfb")) { 1584f7018c21STomi Valkeinen debug("request_mem_region failed!\n"); 1585f7018c21STomi Valkeinen disable_mmio(info->par); 1586f7018c21STomi Valkeinen err = -1; 1587f7018c21STomi Valkeinen goto out_unmap1; 1588f7018c21STomi Valkeinen } 1589f7018c21STomi Valkeinen 15904bdc0d67SChristoph Hellwig info->screen_base = ioremap(tridentfb_fix.smem_start, 1591f7018c21STomi Valkeinen tridentfb_fix.smem_len); 1592f7018c21STomi Valkeinen 1593f7018c21STomi Valkeinen if (!info->screen_base) { 1594f7018c21STomi Valkeinen debug("ioremap failed\n"); 1595f7018c21STomi Valkeinen err = -1; 1596f7018c21STomi Valkeinen goto out_unmap2; 1597f7018c21STomi Valkeinen } 1598f7018c21STomi Valkeinen 1599f7018c21STomi Valkeinen default_par->flatpanel = is_flatpanel(default_par); 1600f7018c21STomi Valkeinen 1601f7018c21STomi Valkeinen if (default_par->flatpanel) 1602f7018c21STomi Valkeinen nativex = get_nativex(default_par); 1603f7018c21STomi Valkeinen 1604f7018c21STomi Valkeinen info->fix = tridentfb_fix; 1605f7018c21STomi Valkeinen info->fbops = &tridentfb_ops; 1606f7018c21STomi Valkeinen info->pseudo_palette = default_par->pseudo_pal; 1607f7018c21STomi Valkeinen 1608f7018c21STomi Valkeinen info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; 1609f7018c21STomi Valkeinen if (!noaccel && default_par->init_accel) { 1610f7018c21STomi Valkeinen info->flags &= ~FBINFO_HWACCEL_DISABLED; 1611f7018c21STomi Valkeinen info->flags |= FBINFO_HWACCEL_COPYAREA; 1612f7018c21STomi Valkeinen info->flags |= FBINFO_HWACCEL_FILLRECT; 1613f7018c21STomi Valkeinen } else 1614f7018c21STomi Valkeinen info->flags |= FBINFO_HWACCEL_DISABLED; 1615f7018c21STomi Valkeinen 1616f7018c21STomi Valkeinen if (is_blade(chip_id) && chip_id != BLADE3D) 1617f7018c21STomi Valkeinen info->flags |= FBINFO_READS_FAST; 1618f7018c21STomi Valkeinen 1619f7018c21STomi Valkeinen info->pixmap.addr = kmalloc(4096, GFP_KERNEL); 1620f7018c21STomi Valkeinen if (!info->pixmap.addr) { 1621f7018c21STomi Valkeinen err = -ENOMEM; 1622f7018c21STomi Valkeinen goto out_unmap2; 1623f7018c21STomi Valkeinen } 1624f7018c21STomi Valkeinen 1625f7018c21STomi Valkeinen info->pixmap.size = 4096; 1626f7018c21STomi Valkeinen info->pixmap.buf_align = 4; 1627f7018c21STomi Valkeinen info->pixmap.scan_align = 1; 1628f7018c21STomi Valkeinen info->pixmap.access_align = 32; 1629f7018c21STomi Valkeinen info->pixmap.flags = FB_PIXMAP_SYSTEM; 16306a5e3bd0SOndrej Zary info->var.bits_per_pixel = 8; 1631f7018c21STomi Valkeinen 1632f7018c21STomi Valkeinen if (default_par->image_blit) { 1633f7018c21STomi Valkeinen info->flags |= FBINFO_HWACCEL_IMAGEBLIT; 1634f7018c21STomi Valkeinen info->pixmap.scan_align = 4; 1635f7018c21STomi Valkeinen } 1636f7018c21STomi Valkeinen 1637f7018c21STomi Valkeinen if (noaccel) { 1638f7018c21STomi Valkeinen printk(KERN_DEBUG "disabling acceleration\n"); 1639f7018c21STomi Valkeinen info->flags |= FBINFO_HWACCEL_DISABLED; 1640f7018c21STomi Valkeinen info->pixmap.scan_align = 1; 1641f7018c21STomi Valkeinen } 1642f7018c21STomi Valkeinen 16436a5e3bd0SOndrej Zary if (tridentfb_setup_ddc_bus(info) == 0) { 16446a5e3bd0SOndrej Zary u8 *edid = fb_ddc_read(&default_par->ddc_adapter); 16456a5e3bd0SOndrej Zary 16466a5e3bd0SOndrej Zary default_par->ddc_registered = true; 16476a5e3bd0SOndrej Zary if (edid) { 16486a5e3bd0SOndrej Zary fb_edid_to_monspecs(edid, &info->monspecs); 16496a5e3bd0SOndrej Zary kfree(edid); 16506a5e3bd0SOndrej Zary if (!info->monspecs.modedb) 16516a5e3bd0SOndrej Zary dev_err(info->device, "error getting mode database\n"); 16526a5e3bd0SOndrej Zary else { 16536a5e3bd0SOndrej Zary const struct fb_videomode *m; 16546a5e3bd0SOndrej Zary 16556a5e3bd0SOndrej Zary fb_videomode_to_modelist(info->monspecs.modedb, 16566a5e3bd0SOndrej Zary info->monspecs.modedb_len, 16576a5e3bd0SOndrej Zary &info->modelist); 16586a5e3bd0SOndrej Zary m = fb_find_best_display(&info->monspecs, 16596a5e3bd0SOndrej Zary &info->modelist); 16606a5e3bd0SOndrej Zary if (m) { 16616a5e3bd0SOndrej Zary fb_videomode_to_var(&info->var, m); 16626a5e3bd0SOndrej Zary /* fill all other info->var's fields */ 16636a5e3bd0SOndrej Zary if (tridentfb_check_var(&info->var, 16646a5e3bd0SOndrej Zary info) == 0) 16656a5e3bd0SOndrej Zary found = true; 16666a5e3bd0SOndrej Zary } 16676a5e3bd0SOndrej Zary } 16686a5e3bd0SOndrej Zary } 16696a5e3bd0SOndrej Zary } 16706a5e3bd0SOndrej Zary 16716a5e3bd0SOndrej Zary if (!mode_option && !found) 16726a5e3bd0SOndrej Zary mode_option = "640x480-8@60"; 16736a5e3bd0SOndrej Zary 16746a5e3bd0SOndrej Zary /* Prepare startup mode */ 16756a5e3bd0SOndrej Zary if (mode_option) { 16766a5e3bd0SOndrej Zary err = fb_find_mode(&info->var, info, mode_option, 16776a5e3bd0SOndrej Zary info->monspecs.modedb, 16786a5e3bd0SOndrej Zary info->monspecs.modedb_len, 16796a5e3bd0SOndrej Zary NULL, info->var.bits_per_pixel); 16806a5e3bd0SOndrej Zary if (!err || err == 4) { 1681f7018c21STomi Valkeinen err = -EINVAL; 16826a5e3bd0SOndrej Zary dev_err(info->device, "mode %s not found\n", 16836a5e3bd0SOndrej Zary mode_option); 16846a5e3bd0SOndrej Zary fb_destroy_modedb(info->monspecs.modedb); 16856a5e3bd0SOndrej Zary info->monspecs.modedb = NULL; 1686f7018c21STomi Valkeinen goto out_unmap2; 1687f7018c21STomi Valkeinen } 16886a5e3bd0SOndrej Zary } 16896a5e3bd0SOndrej Zary 16906a5e3bd0SOndrej Zary fb_destroy_modedb(info->monspecs.modedb); 16916a5e3bd0SOndrej Zary info->monspecs.modedb = NULL; 16926a5e3bd0SOndrej Zary 1693f7018c21STomi Valkeinen err = fb_alloc_cmap(&info->cmap, 256, 0); 1694f7018c21STomi Valkeinen if (err < 0) 1695f7018c21STomi Valkeinen goto out_unmap2; 1696f7018c21STomi Valkeinen 1697f7018c21STomi Valkeinen info->var.activate |= FB_ACTIVATE_NOW; 1698f7018c21STomi Valkeinen info->device = &dev->dev; 1699f7018c21STomi Valkeinen if (register_framebuffer(info) < 0) { 1700f7018c21STomi Valkeinen printk(KERN_ERR "tridentfb: could not register framebuffer\n"); 1701f7018c21STomi Valkeinen fb_dealloc_cmap(&info->cmap); 1702f7018c21STomi Valkeinen err = -EINVAL; 1703f7018c21STomi Valkeinen goto out_unmap2; 1704f7018c21STomi Valkeinen } 1705f7018c21STomi Valkeinen output("fb%d: %s frame buffer device %dx%d-%dbpp\n", 1706f7018c21STomi Valkeinen info->node, info->fix.id, info->var.xres, 1707f7018c21STomi Valkeinen info->var.yres, info->var.bits_per_pixel); 1708f7018c21STomi Valkeinen 1709f7018c21STomi Valkeinen pci_set_drvdata(dev, info); 1710f7018c21STomi Valkeinen return 0; 1711f7018c21STomi Valkeinen 1712f7018c21STomi Valkeinen out_unmap2: 17136a5e3bd0SOndrej Zary if (default_par->ddc_registered) 17146a5e3bd0SOndrej Zary i2c_del_adapter(&default_par->ddc_adapter); 1715f7018c21STomi Valkeinen kfree(info->pixmap.addr); 1716f7018c21STomi Valkeinen if (info->screen_base) 1717f7018c21STomi Valkeinen iounmap(info->screen_base); 1718f7018c21STomi Valkeinen release_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len); 1719f7018c21STomi Valkeinen disable_mmio(info->par); 1720f7018c21STomi Valkeinen out_unmap1: 1721f7018c21STomi Valkeinen if (default_par->io_virt) 1722f7018c21STomi Valkeinen iounmap(default_par->io_virt); 1723f7018c21STomi Valkeinen release_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len); 1724f7018c21STomi Valkeinen framebuffer_release(info); 1725f7018c21STomi Valkeinen return err; 1726f7018c21STomi Valkeinen } 1727f7018c21STomi Valkeinen 1728f7018c21STomi Valkeinen static void trident_pci_remove(struct pci_dev *dev) 1729f7018c21STomi Valkeinen { 1730f7018c21STomi Valkeinen struct fb_info *info = pci_get_drvdata(dev); 1731f7018c21STomi Valkeinen struct tridentfb_par *par = info->par; 1732f7018c21STomi Valkeinen 1733f7018c21STomi Valkeinen unregister_framebuffer(info); 17346a5e3bd0SOndrej Zary if (par->ddc_registered) 17356a5e3bd0SOndrej Zary i2c_del_adapter(&par->ddc_adapter); 1736f7018c21STomi Valkeinen iounmap(par->io_virt); 1737f7018c21STomi Valkeinen iounmap(info->screen_base); 1738f7018c21STomi Valkeinen release_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len); 1739f7018c21STomi Valkeinen release_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len); 1740f7018c21STomi Valkeinen kfree(info->pixmap.addr); 1741f7018c21STomi Valkeinen fb_dealloc_cmap(&info->cmap); 1742f7018c21STomi Valkeinen framebuffer_release(info); 1743f7018c21STomi Valkeinen } 1744f7018c21STomi Valkeinen 1745f7018c21STomi Valkeinen /* List of boards that we are trying to support */ 1746bb6a1818SArvind Yadav static const struct pci_device_id trident_devices[] = { 1747f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, BLADE3D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1748f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1749f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi7D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1750f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1751f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi1D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1752f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, CYBERBLADEAi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1753f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, CYBERBLADEAi1D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1754f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, CYBERBLADEE4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1755f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, TGUI9440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1756f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, TGUI9660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1757f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, IMAGE975, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1758f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, IMAGE985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1759f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, CYBER9320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1760f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, CYBER9388, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1761f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, CYBER9520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1762f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, CYBER9525DVD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1763f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, CYBER9397, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1764f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, CYBER9397DVD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1765f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPAi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1766f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPm8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1767f7018c21STomi Valkeinen {PCI_VENDOR_ID_TRIDENT, CYBERBLADEXPm16, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 1768f7018c21STomi Valkeinen {0,} 1769f7018c21STomi Valkeinen }; 1770f7018c21STomi Valkeinen 1771f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(pci, trident_devices); 1772f7018c21STomi Valkeinen 1773f7018c21STomi Valkeinen static struct pci_driver tridentfb_pci_driver = { 1774f7018c21STomi Valkeinen .name = "tridentfb", 1775f7018c21STomi Valkeinen .id_table = trident_devices, 1776f7018c21STomi Valkeinen .probe = trident_pci_probe, 1777f7018c21STomi Valkeinen .remove = trident_pci_remove, 1778f7018c21STomi Valkeinen }; 1779f7018c21STomi Valkeinen 1780f7018c21STomi Valkeinen /* 1781f7018c21STomi Valkeinen * Parse user specified options (`video=trident:') 1782f7018c21STomi Valkeinen * example: 1783f7018c21STomi Valkeinen * video=trident:800x600,bpp=16,noaccel 1784f7018c21STomi Valkeinen */ 1785f7018c21STomi Valkeinen #ifndef MODULE 1786f7018c21STomi Valkeinen static int __init tridentfb_setup(char *options) 1787f7018c21STomi Valkeinen { 1788f7018c21STomi Valkeinen char *opt; 1789f7018c21STomi Valkeinen if (!options || !*options) 1790f7018c21STomi Valkeinen return 0; 1791f7018c21STomi Valkeinen while ((opt = strsep(&options, ",")) != NULL) { 1792f7018c21STomi Valkeinen if (!*opt) 1793f7018c21STomi Valkeinen continue; 1794f7018c21STomi Valkeinen if (!strncmp(opt, "noaccel", 7)) 1795f7018c21STomi Valkeinen noaccel = 1; 1796f7018c21STomi Valkeinen else if (!strncmp(opt, "fp", 2)) 1797f7018c21STomi Valkeinen fp = 1; 1798f7018c21STomi Valkeinen else if (!strncmp(opt, "crt", 3)) 1799f7018c21STomi Valkeinen fp = 0; 1800f7018c21STomi Valkeinen else if (!strncmp(opt, "bpp=", 4)) 1801f7018c21STomi Valkeinen bpp = simple_strtoul(opt + 4, NULL, 0); 1802f7018c21STomi Valkeinen else if (!strncmp(opt, "center", 6)) 1803f7018c21STomi Valkeinen center = 1; 1804f7018c21STomi Valkeinen else if (!strncmp(opt, "stretch", 7)) 1805f7018c21STomi Valkeinen stretch = 1; 1806f7018c21STomi Valkeinen else if (!strncmp(opt, "memsize=", 8)) 1807f7018c21STomi Valkeinen memsize = simple_strtoul(opt + 8, NULL, 0); 1808f7018c21STomi Valkeinen else if (!strncmp(opt, "memdiff=", 8)) 1809f7018c21STomi Valkeinen memdiff = simple_strtoul(opt + 8, NULL, 0); 1810f7018c21STomi Valkeinen else if (!strncmp(opt, "nativex=", 8)) 1811f7018c21STomi Valkeinen nativex = simple_strtoul(opt + 8, NULL, 0); 1812f7018c21STomi Valkeinen else 1813f7018c21STomi Valkeinen mode_option = opt; 1814f7018c21STomi Valkeinen } 1815f7018c21STomi Valkeinen return 0; 1816f7018c21STomi Valkeinen } 1817f7018c21STomi Valkeinen #endif 1818f7018c21STomi Valkeinen 1819f7018c21STomi Valkeinen static int __init tridentfb_init(void) 1820f7018c21STomi Valkeinen { 1821f7018c21STomi Valkeinen #ifndef MODULE 1822f7018c21STomi Valkeinen char *option = NULL; 1823f7018c21STomi Valkeinen 1824f7018c21STomi Valkeinen if (fb_get_options("tridentfb", &option)) 1825f7018c21STomi Valkeinen return -ENODEV; 1826f7018c21STomi Valkeinen tridentfb_setup(option); 1827f7018c21STomi Valkeinen #endif 1828f7018c21STomi Valkeinen return pci_register_driver(&tridentfb_pci_driver); 1829f7018c21STomi Valkeinen } 1830f7018c21STomi Valkeinen 1831f7018c21STomi Valkeinen static void __exit tridentfb_exit(void) 1832f7018c21STomi Valkeinen { 1833f7018c21STomi Valkeinen pci_unregister_driver(&tridentfb_pci_driver); 1834f7018c21STomi Valkeinen } 1835f7018c21STomi Valkeinen 1836f7018c21STomi Valkeinen module_init(tridentfb_init); 1837f7018c21STomi Valkeinen module_exit(tridentfb_exit); 1838f7018c21STomi Valkeinen 1839f7018c21STomi Valkeinen MODULE_AUTHOR("Jani Monoses <jani@iv.ro>"); 1840f7018c21STomi Valkeinen MODULE_DESCRIPTION("Framebuffer driver for Trident cards"); 1841f7018c21STomi Valkeinen MODULE_LICENSE("GPL"); 1842f7018c21STomi Valkeinen MODULE_ALIAS("cyblafb"); 1843f7018c21STomi Valkeinen 1844