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