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