linux/drivers/video/cyblafb.c
<<
>>
Prefs
   1/*
   2 * Frame buffer driver for Trident Cyberblade/i1 graphics core
   3 *
   4 * Copyright 2005 Knut Petersen <Knut_Petersen@t-online.de>
   5 *
   6 * CREDITS:
   7 *      tridentfb.c by Jani Monoses
   8 *      see files above for further credits
   9 *
  10 */
  11
  12#define CYBLAFB_DEBUG 0
  13#define CYBLAFB_KD_GRAPHICS_QUIRK 1
  14
  15#define CYBLAFB_PIXMAPSIZE 8192
  16
  17#include <linux/module.h>
  18#include <linux/string.h>
  19#include <linux/fb.h>
  20#include <linux/init.h>
  21#include <linux/pci.h>
  22#include <asm/types.h>
  23#include <video/cyblafb.h>
  24
  25#define VERSION "0.62"
  26
  27struct cyblafb_par {
  28        u32 pseudo_pal[16];
  29        struct fb_ops ops;
  30};
  31
  32static struct fb_fix_screeninfo cyblafb_fix __devinitdata = {
  33        .id = "CyBla",
  34        .type = FB_TYPE_PACKED_PIXELS,
  35        .xpanstep = 1,
  36        .ypanstep = 1,
  37        .ywrapstep = 1,
  38        .visual = FB_VISUAL_PSEUDOCOLOR,
  39        .accel = FB_ACCEL_NONE,
  40};
  41
  42static char *mode __devinitdata = NULL;
  43static int bpp __devinitdata = 8;
  44static int ref __devinitdata = 75;
  45static int fp __devinitdata;
  46static int crt __devinitdata;
  47static int memsize __devinitdata;
  48
  49static int basestride;
  50static int vesafb;
  51static int nativex;
  52static int center;
  53static int stretch;
  54static int pciwb = 1;
  55static int pcirb = 1;
  56static int pciwr = 1;
  57static int pcirr = 1;
  58static int disabled;
  59static int verbosity;
  60static int displaytype;
  61
  62static void __iomem *io_virt;   // iospace virtual memory address
  63
  64module_param(mode, charp, 0);
  65module_param(bpp, int, 0);
  66module_param(ref, int, 0);
  67module_param(fp, int, 0);
  68module_param(crt, int, 0);
  69module_param(nativex, int, 0);
  70module_param(center, int, 0);
  71module_param(stretch, int, 0);
  72module_param(pciwb, int, 0);
  73module_param(pcirb, int, 0);
  74module_param(pciwr, int, 0);
  75module_param(pcirr, int, 0);
  76module_param(memsize, int, 0);
  77module_param(verbosity, int, 0);
  78
  79//=========================================
  80//
  81// Well, we have to fix the upper layers.
  82// Until this has been done, we work around
  83// the bugs.
  84//
  85//=========================================
  86
  87#if (CYBLAFB_KD_GRAPHICS_QUIRK && CYBLAFB_DEBUG)
  88        if (disabled) { \
  89                printk("********\n");\
  90                dump_stack();\
  91                return val;\
  92        }
  93
  94#elif CYBLAFB_KD_GRAPHICS_QUIRK
  95#define KD_GRAPHICS_RETURN(val)\
  96        if (disabled) {\
  97                return val;\
  98        }
  99#else
 100#define KD_GRAPHICS_RETURN(val)
 101#endif
 102
 103//=========================================
 104//
 105// Port access macros for memory mapped io
 106//
 107//=========================================
 108
 109#define out8(r, v) writeb(v, io_virt + r)
 110#define out32(r, v) writel(v, io_virt + r)
 111#define in8(r) readb(io_virt + r)
 112#define in32(r) readl(io_virt + r)
 113
 114//======================================
 115//
 116// Hardware access inline functions
 117//
 118//======================================
 119
 120static inline u8 read3X4(u32 reg)
 121{
 122        out8(0x3D4, reg);
 123        return in8(0x3D5);
 124}
 125
 126static inline u8 read3C4(u32 reg)
 127{
 128        out8(0x3C4, reg);
 129        return in8(0x3C5);
 130}
 131
 132static inline u8 read3CE(u32 reg)
 133{
 134        out8(0x3CE, reg);
 135        return in8(0x3CF);
 136}
 137
 138static inline void write3X4(u32 reg, u8 val)
 139{
 140        out8(0x3D4, reg);
 141        out8(0x3D5, val);
 142}
 143
 144static inline void write3C4(u32 reg, u8 val)
 145{
 146        out8(0x3C4, reg);
 147        out8(0x3C5, val);
 148}
 149
 150static inline void write3CE(u32 reg, u8 val)
 151{
 152        out8(0x3CE, reg);
 153        out8(0x3CF, val);
 154}
 155
 156static inline void write3C0(u32 reg, u8 val)
 157{
 158        in8(0x3DA);             // read to reset index
 159        out8(0x3C0, reg);
 160        out8(0x3C0, val);
 161}
 162
 163//=================================================
 164//
 165// Enable memory mapped io and unprotect registers
 166//
 167//=================================================
 168
 169static void enable_mmio(void)
 170{
 171        u8 tmp;
 172
 173        outb(0x0B, 0x3C4);
 174        inb(0x3C5);             // Set NEW mode
 175        outb(SR0E, 0x3C4);      // write enable a lot of extended ports
 176        outb(0x80, 0x3C5);
 177
 178        outb(SR11, 0x3C4);      // write enable those extended ports that
 179        outb(0x87, 0x3C5);      // are not affected by SR0E_New
 180
 181        outb(CR1E, 0x3d4);      // clear write protect bit for port 0x3c2
 182        tmp = inb(0x3d5) & 0xBF;
 183        outb(CR1E, 0x3d4);
 184        outb(tmp, 0x3d5);
 185
 186        outb(CR39, 0x3D4);
 187        outb(inb(0x3D5) | 0x01, 0x3D5); // Enable mmio
 188}
 189
 190//=================================================
 191//
 192// Set pixel clock VCLK1
 193// - multipliers set elswhere
 194// - freq in units of 0.01 MHz
 195//
 196// Hardware bug: SR18 >= 250 is broken for the
 197//               cyberblade/i1
 198//
 199//=================================================
 200
 201static void set_vclk(struct cyblafb_par *par, int freq)
 202{
 203        u32 m, n, k;
 204        int f, fi, d, di;
 205        u8 lo = 0, hi = 0;
 206
 207        d = 2000;
 208        k = freq >= 10000 ? 0 : freq >= 5000 ? 1 : freq >= 2500 ? 2 : 3;
 209        for (m = 0; m < 64; m++)
 210                for (n = 0; n < 250; n++) {
 211                        fi = (int)(((5864727 * (n + 8)) /
 212                                    ((m + 2) * (1 << k))) >> 12);
 213                        if ((di = abs(fi - freq)) < d) {
 214                                d = di;
 215                                f = fi;
 216                                lo = (u8) n;
 217                                hi = (u8) ((k << 6) | m);
 218                        }
 219                }
 220        write3C4(SR19, hi);
 221        write3C4(SR18, lo);
 222        if (verbosity > 0)
 223                output("pixclock = %d.%02d MHz, k/m/n %x %x %x\n",
 224                       freq / 100, freq % 100, (hi & 0xc0) >> 6, hi & 0x3f, lo);
 225}
 226
 227//================================================
 228//
 229// Cyberblade specific Graphics Engine (GE) setup
 230//
 231//================================================
 232
 233static void cyblafb_setup_GE(int pitch, int bpp)
 234{
 235        KD_GRAPHICS_RETURN();
 236
 237        switch (bpp) {
 238        case 8:
 239                basestride = ((pitch >> 3) << 20) | (0 << 29);
 240                break;
 241        case 15:
 242                basestride = ((pitch >> 3) << 20) | (5 << 29);
 243                break;
 244        case 16:
 245                basestride = ((pitch >> 3) << 20) | (1 << 29);
 246                break;
 247        case 24:
 248        case 32:
 249                basestride = ((pitch >> 3) << 20) | (2 << 29);
 250                break;
 251        }
 252
 253        write3X4(CR36, 0x90);   // reset GE
 254        write3X4(CR36, 0x80);   // enable GE
 255        out32(GE24, 1 << 7);    // reset all GE pointers by toggling
 256        out32(GE24, 0);         //   d7 of GE24
 257        write3X4(CR2D, 0x00);   // GE Timinigs, no delays
 258        out32(GE6C, 0);         // Pattern and Style, p 129, ok
 259}
 260
 261//=====================================================================
 262//
 263// Cyberblade specific syncing
 264//
 265//   A timeout might be caused by disabled mmio.
 266//   Cause:
 267//     - bit CR39 & 1 == 0 upon return, X trident driver bug
 268//     - kdm bug (KD_GRAPHICS not set on first switch)
 269//     - kernel design flaw (it believes in the correctness
 270//       of kdm/X
 271//   First we try to sync ignoring that problem, as most of the
 272//   time that will succeed immediately and the enable_mmio()
 273//   would only degrade performance.
 274//
 275//=====================================================================
 276
 277static int cyblafb_sync(struct fb_info *info)
 278{
 279        u32 status, i = 100000;
 280
 281        KD_GRAPHICS_RETURN(0);
 282
 283        while (((status = in32(GE20)) & 0xFe800000) && i != 0)
 284                i--;
 285
 286        if (i == 0) {
 287                enable_mmio();
 288                i = 1000000;
 289                while (((status = in32(GE20)) & 0xFA800000) && i != 0)
 290                        i--;
 291                if (i == 0) {
 292                        output("GE Timeout, status: %x\n", status);
 293                        if (status & 0x80000000)
 294                                output("Bresenham Engine : Busy\n");
 295                        if (status & 0x40000000)
 296                                output("Setup Engine     : Busy\n");
 297                        if (status & 0x20000000)
 298                                output("SP / DPE         : Busy\n");
 299                        if (status & 0x10000000)
 300                                output("Memory Interface : Busy\n");
 301                        if (status & 0x08000000)
 302                                output("Com Lst Proc     : Busy\n");
 303                        if (status & 0x04000000)
 304                                output("Block Write      : Busy\n");
 305                        if (status & 0x02000000)
 306                                output("Command Buffer   : Full\n");
 307                        if (status & 0x01000000)
 308                                output("RESERVED         : Busy\n");
 309                        if (status & 0x00800000)
 310                                output("PCI Write Buffer : Busy\n");
 311                        cyblafb_setup_GE(info->var.xres,
 312                                         info->var.bits_per_pixel);
 313                }
 314        }
 315
 316        return 0;
 317}
 318
 319//==============================
 320//
 321// Cyberblade specific fillrect
 322//
 323//==============================
 324
 325static void cyblafb_fillrect(struct fb_info *info, const struct fb_fillrect *fr)
 326{
 327        u32 bpp = info->var.bits_per_pixel, col, desty, height;
 328
 329        KD_GRAPHICS_RETURN();
 330
 331        switch (bpp) {
 332        default:
 333        case 8:
 334                col = fr->color;
 335                col |= col << 8;
 336                col |= col << 16;
 337                break;
 338        case 16:
 339                col = ((u32 *) (info->pseudo_palette))[fr->color];
 340                col |= col << 16;
 341                break;
 342        case 32:
 343                col = ((u32 *) (info->pseudo_palette))[fr->color];
 344                break;
 345        }
 346
 347        desty = fr->dy;
 348        height = fr->height;
 349        while (height) {
 350                out32(GEB8, basestride | ((desty * info->var.xres_virtual *
 351                                           bpp) >> 6));
 352                out32(GE60, col);
 353                out32(GE48, fr->rop ? 0x66 : ROP_S);
 354                out32(GE44, 0x20000000 | 1 << 19 | 1 << 4 | 2 << 2);
 355                out32(GE08, point(fr->dx, 0));
 356                out32(GE0C, point(fr->dx + fr->width - 1,
 357                                  height > 4096 ? 4095 : height - 1));
 358                if (likely(height <= 4096))
 359                        return;
 360                desty += 4096;
 361                height -= 4096;
 362        }
 363}
 364
 365//================================================
 366//
 367// Cyberblade specific copyarea
 368//
 369// This function silently assumes that it never
 370// will be called with width or height exceeding
 371// 4096.
 372//
 373//================================================
 374
 375static void cyblafb_copyarea(struct fb_info *info, const struct fb_copyarea *ca)
 376{
 377        u32 s1, s2, d1, d2, direction;
 378
 379        KD_GRAPHICS_RETURN();
 380
 381        s1 = point(ca->sx, 0);
 382        s2 = point(ca->sx + ca->width - 1, ca->height - 1);
 383        d1 = point(ca->dx, 0);
 384        d2 = point(ca->dx + ca->width - 1, ca->height - 1);
 385
 386        if ((ca->sy > ca->dy) || ((ca->sy == ca->dy) && (ca->sx > ca->dx)))
 387                direction = 0;
 388        else
 389                direction = 2;
 390
 391        out32(GEB8, basestride | ((ca->dy * info->var.xres_virtual *
 392                                   info->var.bits_per_pixel) >> 6));
 393        out32(GEC8, basestride | ((ca->sy * info->var.xres_virtual *
 394                                   info->var.bits_per_pixel) >> 6));
 395        out32(GE44, 0xa0000000 | 1 << 19 | 1 << 2 | direction);
 396        out32(GE00, direction ? s2 : s1);
 397        out32(GE04, direction ? s1 : s2);
 398        out32(GE08, direction ? d2 : d1);
 399        out32(GE0C, direction ? d1 : d2);
 400}
 401
 402//=======================================================================
 403//
 404// Cyberblade specific imageblit
 405//
 406// Accelerated for the most usual case, blitting 1 - bit deep
 407// character images. Everything else is passed to the generic imageblit
 408// unless it is so insane that it is better to printk an alert.
 409//
 410// Hardware bug: _Never_ blit across pixel column 2048, that will lock
 411// the system. We split those blit requests into three blitting
 412// operations.
 413//
 414//=======================================================================
 415
 416static void cyblafb_imageblit(struct fb_info *info,
 417                              const struct fb_image *image)
 418{
 419        u32 fgcol, bgcol;
 420        u32 *pd = (u32 *) image->data;
 421        u32 bpp = info->var.bits_per_pixel;
 422
 423        KD_GRAPHICS_RETURN();
 424
 425        // Used only for drawing the penguine (image->depth > 1)
 426        if (image->depth != 1) {
 427                cfb_imageblit(info, image);
 428                return;
 429        }
 430        // That should never happen, but it would be fatal
 431        if (image->width == 0 || image->height == 0) {
 432                output("imageblit: width/height 0 detected\n");
 433                return;
 434        }
 435
 436        if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
 437            info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
 438                fgcol = ((u32 *) (info->pseudo_palette))[image->fg_color];
 439                bgcol = ((u32 *) (info->pseudo_palette))[image->bg_color];
 440        } else {
 441                fgcol = image->fg_color;
 442                bgcol = image->bg_color;
 443        }
 444
 445        switch (bpp) {
 446        case 8:
 447                fgcol |= fgcol << 8;
 448                bgcol |= bgcol << 8;
 449        case 16:
 450                fgcol |= fgcol << 16;
 451                bgcol |= bgcol << 16;
 452        default:
 453                break;
 454        }
 455
 456        out32(GEB8, basestride | ((image->dy * info->var.xres_virtual *
 457                                   bpp) >> 6));
 458        out32(GE60, fgcol);
 459        out32(GE64, bgcol);
 460
 461        if (!(image->dx < 2048 && (image->dx + image->width - 1) >= 2048)) {
 462                u32 dds = ((image->width + 31) >> 5) * image->height;
 463                out32(GE44, 0xa0000000 | 1 << 20 | 1 << 19);
 464                out32(GE08, point(image->dx, 0));
 465                out32(GE0C, point(image->dx + image->width - 1,
 466                                  image->height - 1));
 467                while (dds--)
 468                        out32(GE9C, *pd++);
 469        } else {
 470                int i, j;
 471                u32 ddstotal = (image->width + 31) >> 5;
 472                u32 ddsleft = (2048 - image->dx + 31) >> 5;
 473                u32 skipleft = ddstotal - ddsleft;
 474
 475                out32(GE44, 0xa0000000 | 1 << 20 | 1 << 19);
 476                out32(GE08, point(image->dx, 0));
 477                out32(GE0C, point(2048 - 1, image->height - 1));
 478                for (i = 0; i < image->height; i++) {
 479                        for (j = 0; j < ddsleft; j++)
 480                                out32(GE9C, *pd++);
 481                        pd += skipleft;
 482                }
 483
 484                if (image->dx % 32) {
 485                        out32(GE44, 0xa0000000 | 1 << 20 | 1 << 19);
 486                        out32(GE08, point(2048, 0));
 487                        if (image->width > ddsleft << 5)
 488                                out32(GE0C, point(image->dx + (ddsleft << 5) -
 489                                                  1, image->height - 1));
 490                        else
 491                                out32(GE0C, point(image->dx + image->width - 1,
 492                                                  image->height - 1));
 493                        pd = ((u32 *) image->data) + ddstotal - skipleft - 1;
 494                        for (i = 0; i < image->height; i++) {
 495                                out32(GE9C, swab32(swab32(*pd) << ((32 -
 496                                            (image->dx & 31)) & 31)));
 497                                pd += ddstotal;
 498                        }
 499                }
 500
 501                if (skipleft) {
 502                        out32(GE44, 0xa0000000 | 1 << 20 | 1 << 19);
 503                        out32(GE08, point(image->dx + (ddsleft << 5), 0));
 504                        out32(GE0C, point(image->dx + image->width - 1,
 505                                          image->height - 1));
 506                        pd = (u32 *) image->data;
 507                        for (i = 0; i < image->height; i++) {
 508                                pd += ddsleft;
 509                                for (j = 0; j < skipleft; j++)
 510                                        out32(GE9C, *pd++);
 511                        }
 512                }
 513        }
 514}
 515
 516//==========================================================
 517//
 518// Check if video mode is acceptable. We change var->??? if
 519// video mode is slightly off or return error otherwise.
 520// info->??? must not be changed!
 521//
 522//==========================================================
 523
 524static int cyblafb_check_var(struct fb_var_screeninfo *var,
 525                             struct fb_info *info)
 526{
 527        int bpp = var->bits_per_pixel;
 528
 529        //
 530        // we try to support 8, 16, 24 and 32 bpp modes,
 531        // default to 8
 532        //
 533        // there is a 24 bpp mode, but for now we change requests to 32 bpp
 534        // (This is what tridentfb does ... will be changed in the future)
 535        //
 536        //
 537        if (bpp % 8 != 0 || bpp < 8 || bpp > 32)
 538                bpp = 8;
 539        if (bpp == 24)
 540                bpp = var->bits_per_pixel = 32;
 541
 542        //
 543        // interlaced modes are broken, fail if one is requested
 544        //
 545        if (var->vmode & FB_VMODE_INTERLACED)
 546                return -EINVAL;
 547
 548        //
 549        // fail if requested resolution is higher than physical
 550        // flatpanel resolution
 551        //
 552        if ((displaytype == DISPLAY_FP) && nativex && var->xres > nativex)
 553                return -EINVAL;
 554
 555        //
 556        // we do not allow vclk to exceed 230 MHz. If the requested
 557        // vclk is too high, we default to 200 MHz
 558        //
 559        if ((bpp == 32 ? 200000000 : 100000000) / var->pixclock > 23000)
 560                var->pixclock = (bpp == 32 ? 200000000 : 100000000) / 20000;
 561
 562        //
 563        // enforce (h|v)sync_len limits
 564        //
 565        var->hsync_len &= ~7;
 566        if(var->hsync_len > 248)
 567                var->hsync_len = 248;
 568
 569        var->vsync_len &= 15;
 570
 571        //
 572        // Enforce horizontal and vertical hardware limits.
 573        // 1600x1200 is mentioned as a maximum, but higher resolutions could
 574        // work with slow refresh, small margins and short sync.
 575        //
 576        var->xres &= ~7;
 577
 578        if (((var->xres + var->left_margin + var->right_margin +
 579                        var->hsync_len) > (bpp == 32 ? 2040 : 4088)) ||
 580                        ((var->yres + var->upper_margin + var->lower_margin +
 581                        var->vsync_len) > 2047))
 582                return -EINVAL;
 583
 584        if ((var->xres > 1600) || (var->yres > 1200))
 585                output("Mode %dx%d exceeds documented limits.\n",
 586                                           var->xres, var->yres);
 587        //
 588        // try to be smart about (x|y)res_virtual problems.
 589        //
 590        if (var->xres > var->xres_virtual)
 591                var->xres_virtual = var->xres;
 592        if (var->yres > var->yres_virtual)
 593                var->yres_virtual = var->yres;
 594
 595        if (bpp == 8 || bpp == 16) {
 596                if (var->xres_virtual > 4088)
 597                        var->xres_virtual = 4088;
 598        } else {
 599                if (var->xres_virtual > 2040)
 600                        var->xres_virtual = 2040;
 601        }
 602        var->xres_virtual &= ~7;
 603        while (var->xres_virtual * var->yres_virtual * bpp / 8 >
 604               info->fix.smem_len) {
 605                if (var->yres_virtual > var->yres)
 606                        var->yres_virtual--;
 607                else if (var->xres_virtual > var->xres)
 608                        var->xres_virtual -= 8;
 609                else
 610                        return -EINVAL;
 611        }
 612
 613        switch (bpp) {
 614        case 8:
 615                var->red.offset = 0;
 616                var->green.offset = 0;
 617                var->blue.offset = 0;
 618                var->red.length = 6;
 619                var->green.length = 6;
 620                var->blue.length = 6;
 621                break;
 622        case 16:
 623                var->red.offset = 11;
 624                var->green.offset = 5;
 625                var->blue.offset = 0;
 626                var->red.length = 5;
 627                var->green.length = 6;
 628                var->blue.length = 5;
 629                break;
 630        case 32:
 631                var->red.offset = 16;
 632                var->green.offset = 8;
 633                var->blue.offset = 0;
 634                var->red.length = 8;
 635                var->green.length = 8;
 636                var->blue.length = 8;
 637                break;
 638        default:
 639                return -EINVAL;
 640        }
 641
 642        return 0;
 643}
 644
 645//=====================================================================
 646//
 647// Pan the display
 648//
 649// The datasheets defines crt start address to be 20 bits wide and
 650// to be programmed to CR0C, CR0D, CR1E and CR27. Actually there is
 651// CR2B[5] as an undocumented extension bit. Epia BIOS 2.07 does use
 652// it, so it is also safe to be used here. BTW: datasheet CR0E on page
 653// 90 really is CR1E, the real CRE is documented on page 72.
 654//
 655// BUT:
 656//
 657// As of internal version 0.60 we do not use vga panning any longer.
 658// Vga panning did not allow us the use of all available video memory
 659// and thus prevented ywrap scrolling. We do use the "right view"
 660// register now.
 661//
 662//
 663//=====================================================================
 664
 665static int cyblafb_pan_display(struct fb_var_screeninfo *var,
 666                               struct fb_info *info)
 667{
 668        KD_GRAPHICS_RETURN(0);
 669
 670        info->var.xoffset = var->xoffset;
 671        info->var.yoffset = var->yoffset;
 672        out32(GE10, 0x80000000 | ((var->xoffset + (var->yoffset *
 673                    var->xres_virtual)) * var->bits_per_pixel / 32));
 674        return 0;
 675}
 676
 677//============================================
 678//
 679// This will really help in case of a bug ...
 680// dump most gaphics core registers.
 681//
 682//============================================
 683
 684static void regdump(struct cyblafb_par *par)
 685{
 686        int i;
 687
 688        if (verbosity < 2)
 689                return;
 690
 691        printk("\n");
 692        for (i = 0; i <= 0xff; i++) {
 693                outb(i, 0x3d4);
 694                printk("CR%02x=%02x ", i, inb(0x3d5));
 695                if (i % 16 == 15)
 696                        printk("\n");
 697        }
 698
 699        outb(0x30, 0x3ce);
 700        outb(inb(0x3cf) | 0x40, 0x3cf);
 701        for (i = 0; i <= 0x1f; i++) {
 702                if (i == 0 || (i > 2 && i < 8) || i == 0x10 || i == 0x11
 703                    || i == 0x16) {
 704                        outb(i, 0x3d4);
 705                        printk("CR%02x=%02x ", i, inb(0x3d5));
 706                } else
 707                        printk("------- ");
 708                if (i % 16 == 15)
 709                        printk("\n");
 710        }
 711        outb(0x30, 0x3ce);
 712        outb(inb(0x3cf) & 0xbf, 0x3cf);
 713
 714        printk("\n");
 715        for (i = 0; i <= 0x7f; i++) {
 716                outb(i, 0x3ce);
 717                printk("GR%02x=%02x ", i, inb(0x3cf));
 718                if (i % 16 == 15)
 719                        printk("\n");
 720        }
 721
 722        printk("\n");
 723        for (i = 0; i <= 0xff; i++) {
 724                outb(i, 0x3c4);
 725                printk("SR%02x=%02x ", i, inb(0x3c5));
 726                if (i % 16 == 15)
 727                        printk("\n");
 728        }
 729
 730        printk("\n");
 731        for (i = 0; i <= 0x1F; i++) {
 732                inb(0x3da);     // next access is index!
 733                outb(i, 0x3c0);
 734                printk("AR%02x=%02x ", i, inb(0x3c1));
 735                if (i % 16 == 15)
 736                        printk("\n");
 737        }
 738        printk("\n");
 739
 740        inb(0x3DA);             // reset internal flag to 3c0 index
 741        outb(0x20, 0x3C0);      // enable attr
 742
 743        return;
 744}
 745
 746//=======================================================================
 747//
 748// Save State
 749//
 750// This function is called while a switch to KD_TEXT is in progress,
 751// before any of the other functions are called.
 752//
 753//=======================================================================
 754
 755static void cyblafb_save_state(struct fb_info *info)
 756{
 757        struct cyblafb_par *par = info->par;
 758        if (verbosity > 0)
 759                output("Switching to KD_TEXT\n");
 760        disabled = 0;
 761        regdump(par);
 762        enable_mmio();
 763        return;
 764}
 765
 766//=======================================================================
 767//
 768// Restore State
 769//
 770// This function is called while a switch to KD_GRAPHICS is in progress,
 771// We have to turn on vga style panning registers again because the
 772// trident driver of X does not know about GE10.
 773//
 774//=======================================================================
 775
 776static void cyblafb_restore_state(struct fb_info *info)
 777{
 778        if (verbosity > 0)
 779                output("Switching to KD_GRAPHICS\n");
 780        out32(GE10, 0);
 781        disabled = 1;
 782        return;
 783}
 784
 785//======================================
 786//
 787// Set hardware to requested video mode
 788//
 789//======================================
 790
 791static int cyblafb_set_par(struct fb_info *info)
 792{
 793        struct cyblafb_par *par = info->par;
 794        u32 htotal, hdispend, hsyncstart, hsyncend, hblankstart,
 795            hblankend, preendfetch, vtotal, vdispend, vsyncstart,
 796            vsyncend, vblankstart, vblankend;
 797        struct fb_var_screeninfo *var = &info->var;
 798        int bpp = var->bits_per_pixel;
 799        int i;
 800
 801        KD_GRAPHICS_RETURN(0);
 802
 803        if (verbosity > 0)
 804                output("Switching to new mode: "
 805                       "fbset -g %d %d %d %d %d -t %d %d %d %d %d %d %d\n",
 806                       var->xres, var->yres, var->xres_virtual,
 807                       var->yres_virtual, var->bits_per_pixel, var->pixclock,
 808                       var->left_margin, var->right_margin, var->upper_margin,
 809                       var->lower_margin, var->hsync_len, var->vsync_len);
 810
 811        htotal = (var->xres + var->left_margin + var->right_margin +
 812                  var->hsync_len) / 8 - 5;
 813        hdispend = var->xres / 8 - 1;
 814        hsyncstart = (var->xres + var->right_margin) / 8;
 815        hsyncend = var->hsync_len / 8;
 816        hblankstart = hdispend + 1;
 817        hblankend = htotal + 3; // should be htotal + 5, bios does it this way
 818        preendfetch = ((var->xres >> 3) + 1) * ((bpp + 1) >> 3);
 819
 820        vtotal = var->yres + var->upper_margin + var->lower_margin +
 821                                                        var->vsync_len - 2;
 822        vdispend = var->yres - 1;
 823        vsyncstart = var->yres + var->lower_margin;
 824        vblankstart = var->yres;
 825        vblankend = vtotal; // should be vtotal + 2, but bios does it this way
 826        vsyncend = var->vsync_len;
 827
 828        enable_mmio();          // necessary! ... check X ...
 829
 830        write3X4(CR11, read3X4(CR11) & 0x7F);   // unlock cr00 .. cr07
 831
 832        write3CE(GR30, 8);
 833
 834        if ((displaytype == DISPLAY_FP) && var->xres < nativex) {
 835
 836                // stretch or center ?
 837
 838                out8(0x3C2, 0xEB);
 839
 840                write3CE(GR30, read3CE(GR30) | 0x81);   // shadow mode on
 841
 842                if (center) {
 843                        write3CE(GR52, (read3CE(GR52) & 0x7C) | 0x80);
 844                        write3CE(GR53, (read3CE(GR53) & 0x7C) | 0x80);
 845                } else if (stretch) {
 846                        write3CE(GR5D, 0);
 847                        write3CE(GR52, (read3CE(GR52) & 0x7C) | 1);
 848                        write3CE(GR53, (read3CE(GR53) & 0x7C) | 1);
 849                }
 850
 851        } else {
 852                out8(0x3C2, 0x2B);
 853                write3CE(GR30, 8);
 854        }
 855
 856        //
 857        // Setup CRxx regs
 858        //
 859
 860        write3X4(CR00, htotal & 0xFF);
 861        write3X4(CR01, hdispend & 0xFF);
 862        write3X4(CR02, hblankstart & 0xFF);
 863        write3X4(CR03, hblankend & 0x1F);
 864        write3X4(CR04, hsyncstart & 0xFF);
 865        write3X4(CR05, (hsyncend & 0x1F) | ((hblankend & 0x20) << 2));
 866        write3X4(CR06, vtotal & 0xFF);
 867        write3X4(CR07, (vtotal & 0x100) >> 8 |
 868                       (vdispend & 0x100) >> 7 |
 869                       (vsyncstart & 0x100) >> 6 |
 870                       (vblankstart & 0x100) >> 5 |
 871                       0x10 |
 872                       (vtotal & 0x200) >> 4 |
 873                       (vdispend & 0x200) >> 3 | (vsyncstart & 0x200) >> 2);
 874        write3X4(CR08, 0);
 875        write3X4(CR09, (vblankstart & 0x200) >> 4 | 0x40 |      // FIX !!!
 876                       ((info->var.vmode & FB_VMODE_DOUBLE) ? 0x80 : 0));
 877        write3X4(CR0A, 0);      // Init to some reasonable default
 878        write3X4(CR0B, 0);      // Init to some reasonable default
 879        write3X4(CR0C, 0);      // Offset 0
 880        write3X4(CR0D, 0);      // Offset 0
 881        write3X4(CR0E, 0);      // Init to some reasonable default
 882        write3X4(CR0F, 0);      // Init to some reasonable default
 883        write3X4(CR10, vsyncstart & 0xFF);
 884        write3X4(CR11, (vsyncend & 0x0F));
 885        write3X4(CR12, vdispend & 0xFF);
 886        write3X4(CR13, ((info->var.xres_virtual * bpp) / (4 * 16)) & 0xFF);
 887        write3X4(CR14, 0x40);   // double word mode
 888        write3X4(CR15, vblankstart & 0xFF);
 889        write3X4(CR16, vblankend & 0xFF);
 890        write3X4(CR17, 0xE3);
 891        write3X4(CR18, 0xFF);
 892        //       CR19: needed for interlaced modes ... ignore it for now
 893        write3X4(CR1A, 0x07);   // Arbitration Control Counter 1
 894        write3X4(CR1B, 0x07);   // Arbitration Control Counter 2
 895        write3X4(CR1C, 0x07);   // Arbitration Control Counter 3
 896        write3X4(CR1D, 0x00);   // Don't know, doesn't hurt ; -)
 897        write3X4(CR1E, (info->var.vmode & FB_VMODE_INTERLACED) ? 0x84 : 0x80);
 898        //       CR1F: do not set, contains BIOS info about memsize
 899        write3X4(CR20, 0x20);   // enabe wr buf, disable 16bit planar mode
 900        write3X4(CR21, 0x20);   // enable linear memory access
 901        //       CR22: RO cpu latch readback
 902        //       CR23: ???
 903        //       CR24: RO AR flag state
 904        //       CR25: RAMDAC rw timing, pclk buffer tristate control ????
 905        //       CR26: ???
 906        write3X4(CR27, (vdispend & 0x400) >> 6 |
 907                       (vsyncstart & 0x400) >> 5 |
 908                       (vblankstart & 0x400) >> 4 |
 909                       (vtotal & 0x400) >> 3 |
 910                       0x8);
 911        //       CR28: ???
 912        write3X4(CR29, (read3X4(CR29) & 0xCF) | ((((info->var.xres_virtual *
 913                        bpp) / (4 * 16)) & 0x300) >> 4));
 914        write3X4(CR2A, read3X4(CR2A) | 0x40);
 915        write3X4(CR2B, (htotal & 0x100) >> 8 |
 916                       (hdispend & 0x100) >> 7 |
 917                       // (0x00 & 0x100) >> 6 |   hinterlace para bit 8 ???
 918                       (hsyncstart & 0x100) >> 5 |
 919                       (hblankstart & 0x100) >> 4);
 920        //       CR2C: ???
 921        //       CR2D: initialized in cyblafb_setup_GE()
 922        write3X4(CR2F, 0x92);   // conservative, better signal quality
 923        //       CR30: reserved
 924        //       CR31: reserved
 925        //       CR32: reserved
 926        //       CR33: reserved
 927        //       CR34: disabled in CR36
 928        //       CR35: disabled in CR36
 929        //       CR36: initialized in cyblafb_setup_GE
 930        //       CR37: i2c, ignore for now
 931        write3X4(CR38, (bpp == 8) ? 0x00 :      //
 932                       (bpp == 16) ? 0x05 :     // highcolor
 933                       (bpp == 24) ? 0x29 :     // packed 24bit truecolor
 934                       (bpp == 32) ? 0x09 : 0); // truecolor, 16 bit pixelbus
 935        write3X4(CR39, 0x01 |   // MMIO enable
 936                       (pcirb ? 0x02 : 0) |     // pci read burst enable
 937                       (pciwb ? 0x04 : 0));     // pci write burst enable
 938        write3X4(CR55, 0x1F | // pci clocks * 2 for STOP# during 1st data phase
 939                       (pcirr ? 0x40 : 0) |     // pci read retry enable
 940                       (pciwr ? 0x80 : 0));     // pci write retry enable
 941        write3X4(CR56, preendfetch >> 8 < 2 ? (preendfetch >> 8 & 0x01) | 2
 942                                            : 0);
 943        write3X4(CR57, preendfetch >> 8 < 2 ? preendfetch & 0xff : 0);
 944        write3X4(CR58, 0x82);   // Bios does this .... don't know more
 945        //
 946        // Setup SRxx regs
 947        //
 948        write3C4(SR00, 3);
 949        write3C4(SR01, 1);      //set char clock 8 dots wide
 950        write3C4(SR02, 0x0F);   //enable 4 maps needed in chain4 mode
 951        write3C4(SR03, 0);      //no character map select
 952        write3C4(SR04, 0x0E);   //memory mode: ext mem, even, chain4
 953
 954        out8(0x3C4, 0x0b);
 955        in8(0x3C5);             // Set NEW mode
 956        write3C4(SR0D, 0x00);   // test ... check
 957
 958        set_vclk(par, (bpp == 32 ? 200000000 : 100000000)
 959                                        / info->var.pixclock);  //SR18, SR19
 960
 961        //
 962        // Setup GRxx regs
 963        //
 964        write3CE(GR00, 0x00);   // test ... check
 965        write3CE(GR01, 0x00);   // test ... check
 966        write3CE(GR02, 0x00);   // test ... check
 967        write3CE(GR03, 0x00);   // test ... check
 968        write3CE(GR04, 0x00);   // test ... check
 969        write3CE(GR05, 0x40);   // no CGA compat, allow 256 col
 970        write3CE(GR06, 0x05);   // graphics mode
 971        write3CE(GR07, 0x0F);   // planes?
 972        write3CE(GR08, 0xFF);   // test ... check
 973        write3CE(GR0F, (bpp == 32) ? 0x1A : 0x12); // vclk / 2 if 32bpp, chain4
 974        write3CE(GR20, 0xC0);   // test ... check
 975        write3CE(GR2F, 0xA0);   // PCLK = VCLK, no skew,
 976
 977        //
 978        // Setup ARxx regs
 979        //
 980        for (i = 0; i < 0x10; i++)      // set AR00 .. AR0f
 981                write3C0(i, i);
 982        write3C0(AR10, 0x41);   // graphics mode and support 256 color modes
 983        write3C0(AR12, 0x0F);   // planes
 984        write3C0(AR13, 0);      // horizontal pel panning
 985        in8(0x3DA);             // reset internal flag to 3c0 index
 986        out8(0x3C0, 0x20);      // enable attr
 987
 988        //
 989        // Setup hidden RAMDAC command register
 990        //
 991        in8(0x3C8);             // these reads are
 992        in8(0x3C6);             // necessary to
 993        in8(0x3C6);             // unmask the RAMDAC
 994        in8(0x3C6);             // command reg, otherwise
 995        in8(0x3C6);             // we would write the pixelmask reg!
 996        out8(0x3C6, (bpp == 8) ? 0x00 : // 256 colors
 997             (bpp == 15) ? 0x10 :       //
 998             (bpp == 16) ? 0x30 :       // hicolor
 999             (bpp == 24) ? 0xD0 :       // truecolor
1000             (bpp == 32) ? 0xD0 : 0);   // truecolor
1001        in8(0x3C8);
1002
1003        //
1004        // GR31 is not mentioned in the datasheet
1005        //
1006        if (displaytype == DISPLAY_FP)
1007                write3CE(GR31, (read3CE(GR31) & 0x8F) |
1008                         ((info->var.yres > 1024) ? 0x50 :
1009                          (info->var.yres > 768) ? 0x30 :
1010                          (info->var.yres > 600) ? 0x20 :
1011                          (info->var.yres > 480) ? 0x10 : 0));
1012
1013        info->fix.visual = (bpp == 8) ? FB_VISUAL_PSEUDOCOLOR
1014                                      : FB_VISUAL_TRUECOLOR;
1015        info->fix.line_length = info->var.xres_virtual * (bpp >> 3);
1016        info->cmap.len = (bpp == 8) ? 256 : 16;
1017
1018        //
1019        // init acceleration engine
1020        //
1021        cyblafb_setup_GE(info->var.xres_virtual, info->var.bits_per_pixel);
1022
1023        //
1024        // Set/clear flags to allow proper scroll mode selection.
1025        //
1026        if (var->xres == var->xres_virtual)
1027                info->flags &= ~FBINFO_HWACCEL_XPAN;
1028        else
1029                info->flags |= FBINFO_HWACCEL_XPAN;
1030
1031        if (var->yres == var->yres_virtual)
1032                info->flags &= ~FBINFO_HWACCEL_YPAN;
1033        else
1034                info->flags |= FBINFO_HWACCEL_YPAN;
1035
1036        if (info->fix.smem_len !=
1037            var->xres_virtual * var->yres_virtual * bpp / 8)
1038                info->flags &= ~FBINFO_HWACCEL_YWRAP;
1039        else
1040                info->flags |= FBINFO_HWACCEL_YWRAP;
1041
1042        regdump(par);
1043
1044        return 0;
1045}
1046
1047//========================
1048//
1049// Set one color register
1050//
1051//========================
1052
1053static int cyblafb_setcolreg(unsigned regno, unsigned red, unsigned green,
1054                             unsigned blue, unsigned transp,
1055                             struct fb_info *info)
1056{
1057        int bpp = info->var.bits_per_pixel;
1058
1059        KD_GRAPHICS_RETURN(0);
1060
1061        if (regno >= info->cmap.len)
1062                return 1;
1063
1064        if (bpp == 8) {
1065                out8(0x3C6, 0xFF);
1066                out8(0x3C8, regno);
1067                out8(0x3C9, red >> 10);
1068                out8(0x3C9, green >> 10);
1069                out8(0x3C9, blue >> 10);
1070
1071        } else if (regno < 16) {
1072                if (bpp == 16)  // RGB 565
1073                        ((u32 *) info->pseudo_palette)[regno] =
1074                                (red & 0xF800) |
1075                                ((green & 0xFC00) >> 5) |
1076                                ((blue & 0xF800) >> 11);
1077                else if (bpp == 32)     // ARGB 8888
1078                        ((u32 *) info->pseudo_palette)[regno] =
1079                                ((transp & 0xFF00) << 16) |
1080                                ((red & 0xFF00) << 8) |
1081                                ((green & 0xFF00)) | ((blue & 0xFF00) >> 8);
1082        }
1083
1084        return 0;
1085}
1086
1087//==========================================================
1088//
1089// Try blanking the screen. For flat panels it does nothing
1090//
1091//==========================================================
1092
1093static int cyblafb_blank(int blank_mode, struct fb_info *info)
1094{
1095        unsigned char PMCont, DPMSCont;
1096
1097        KD_GRAPHICS_RETURN(0);
1098
1099        if (displaytype == DISPLAY_FP)
1100                return 0;
1101
1102        out8(0x83C8, 0x04);     // DPMS Control
1103        PMCont = in8(0x83C6) & 0xFC;
1104
1105        DPMSCont = read3CE(GR23) & 0xFC;
1106
1107        switch (blank_mode) {
1108        case FB_BLANK_UNBLANK:  // Screen: On, HSync: On, VSync: On
1109        case FB_BLANK_NORMAL:   // Screen: Off, HSync: On, VSync: On
1110                PMCont |= 0x03;
1111                DPMSCont |= 0x00;
1112                break;
1113        case FB_BLANK_HSYNC_SUSPEND:    // Screen: Off, HSync: Off, VSync: On
1114                PMCont |= 0x02;
1115                DPMSCont |= 0x01;
1116                break;
1117        case FB_BLANK_VSYNC_SUSPEND:    // Screen: Off, HSync: On, VSync: Off
1118                PMCont |= 0x02;
1119                DPMSCont |= 0x02;
1120                break;
1121        case FB_BLANK_POWERDOWN:        // Screen: Off, HSync: Off, VSync: Off
1122                PMCont |= 0x00;
1123                DPMSCont |= 0x03;
1124                break;
1125        }
1126
1127        write3CE(GR23, DPMSCont);
1128        out8(0x83C8, 4);
1129        out8(0x83C6, PMCont);
1130        //
1131        // let fbcon do a softblank for us
1132        //
1133        return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1134}
1135
1136static struct fb_ops cyblafb_ops __devinitdata = {
1137        .owner = THIS_MODULE,
1138        .fb_setcolreg = cyblafb_setcolreg,
1139        .fb_pan_display = cyblafb_pan_display,
1140        .fb_blank = cyblafb_blank,
1141        .fb_check_var = cyblafb_check_var,
1142        .fb_set_par = cyblafb_set_par,
1143        .fb_fillrect = cyblafb_fillrect,
1144        .fb_copyarea = cyblafb_copyarea,
1145        .fb_imageblit = cyblafb_imageblit,
1146        .fb_sync = cyblafb_sync,
1147        .fb_restore_state = cyblafb_restore_state,
1148        .fb_save_state = cyblafb_save_state,
1149};
1150
1151//==========================================================================
1152//
1153// getstartupmode() decides about the inital video mode
1154//
1155// There is no reason to use modedb, a lot of video modes there would
1156// need altered timings to display correctly. So I decided that it is much
1157// better to provide a limited optimized set of modes plus the option of
1158// using the mode in effect at startup time (might be selected using the
1159// vga=??? parameter). After that the user might use fbset to select any
1160// mode he likes, check_var will not try to alter geometry parameters as
1161// it would be necessary otherwise.
1162//
1163//==========================================================================
1164
1165static int __devinit getstartupmode(struct fb_info *info)
1166{
1167        u32 htotal, hdispend, hsyncstart, hsyncend, hblankstart, hblankend,
1168            vtotal, vdispend, vsyncstart, vsyncend, vblankstart, vblankend,
1169            cr00, cr01, cr02, cr03, cr04, cr05, cr2b,
1170            cr06, cr07, cr09, cr10, cr11, cr12, cr15, cr16, cr27,
1171            cr38, sr0d, sr18, sr19, gr0f, fi, pxclkdiv, vclkdiv, tmp, i;
1172
1173        struct modus {
1174                int xres; int vxres; int yres; int vyres;
1175                int bpp; int pxclk;
1176                int left_margin; int right_margin;
1177                int upper_margin; int lower_margin;
1178                int hsync_len; int vsync_len;
1179        } modedb[5] = {
1180                {
1181                0, 2048, 0, 4096, 0, 0, 0, 0, 0, 0, 0, 0}, {
1182                640, 2048, 480, 4096, 0, 0, -40, 24, 17, 0, 216, 3}, {
1183                800, 2048, 600, 4096, 0, 0, 96, 24, 14, 0, 136, 11}, {
1184                1024, 2048, 768, 4096, 0, 0, 144, 24, 29, 0, 120, 3}, {
1185                1280, 2048, 1024, 4096, 0, 0, 232, 16, 39, 0, 160, 3}
1186        };
1187
1188        outb(0x00, 0x3d4); cr00 = inb(0x3d5);
1189        outb(0x01, 0x3d4); cr01 = inb(0x3d5);
1190        outb(0x02, 0x3d4); cr02 = inb(0x3d5);
1191        outb(0x03, 0x3d4); cr03 = inb(0x3d5);
1192        outb(0x04, 0x3d4); cr04 = inb(0x3d5);
1193        outb(0x05, 0x3d4); cr05 = inb(0x3d5);
1194        outb(0x06, 0x3d4); cr06 = inb(0x3d5);
1195        outb(0x07, 0x3d4); cr07 = inb(0x3d5);
1196        outb(0x09, 0x3d4); cr09 = inb(0x3d5);
1197        outb(0x10, 0x3d4); cr10 = inb(0x3d5);
1198        outb(0x11, 0x3d4); cr11 = inb(0x3d5);
1199        outb(0x12, 0x3d4); cr12 = inb(0x3d5);
1200        outb(0x15, 0x3d4); cr15 = inb(0x3d5);
1201        outb(0x16, 0x3d4); cr16 = inb(0x3d5);
1202        outb(0x27, 0x3d4); cr27 = inb(0x3d5);
1203        outb(0x2b, 0x3d4); cr2b = inb(0x3d5);
1204        outb(0x38, 0x3d4); cr38 = inb(0x3d5);
1205
1206        outb(0x0b, 0x3c4);
1207        inb(0x3c5);
1208
1209        outb(0x0d, 0x3c4); sr0d = inb(0x3c5);
1210        outb(0x18, 0x3c4); sr18 = inb(0x3c5);
1211        outb(0x19, 0x3c4); sr19 = inb(0x3c5);
1212        outb(0x0f, 0x3ce); gr0f = inb(0x3cf);
1213
1214        htotal = cr00 | (cr2b & 0x01) << 8;
1215        hdispend = cr01 | (cr2b & 0x02) << 7;
1216        hblankstart = cr02 | (cr2b & 0x10) << 4;
1217        hblankend = (cr03 & 0x1f) | (cr05 & 0x80) >> 2;
1218        hsyncstart = cr04 | (cr2b & 0x08) << 5;
1219        hsyncend = cr05 & 0x1f;
1220
1221        modedb[0].xres = hblankstart * 8;
1222        modedb[0].hsync_len = hsyncend * 8;
1223        modedb[0].right_margin = hsyncstart * 8 - modedb[0].xres;
1224        modedb[0].left_margin = (htotal + 5) * 8 - modedb[0].xres -
1225            modedb[0].right_margin - modedb[0].hsync_len;
1226
1227        vtotal = cr06 | (cr07 & 0x01) << 8 | (cr07 & 0x20) << 4
1228            | (cr27 & 0x80) << 3;
1229        vdispend = cr12 | (cr07 & 0x02) << 7 | (cr07 & 0x40) << 3
1230            | (cr27 & 0x10) << 6;
1231        vsyncstart = cr10 | (cr07 & 0x04) << 6 | (cr07 & 0x80) << 2
1232            | (cr27 & 0x20) << 5;
1233        vsyncend = cr11 & 0x0f;
1234        vblankstart = cr15 | (cr07 & 0x08) << 5 | (cr09 & 0x20) << 4
1235            | (cr27 & 0x40) << 4;
1236        vblankend = cr16;
1237
1238        modedb[0].yres = vdispend + 1;
1239        modedb[0].vsync_len = vsyncend;
1240        modedb[0].lower_margin = vsyncstart - modedb[0].yres;
1241        modedb[0].upper_margin = vtotal - modedb[0].yres -
1242            modedb[0].lower_margin - modedb[0].vsync_len + 2;
1243
1244        tmp = cr38 & 0x3c;
1245        modedb[0].bpp = tmp == 0 ? 8 : tmp == 4 ? 16 : tmp == 28 ? 24 :
1246            tmp == 8 ? 32 : 8;
1247
1248        fi = ((5864727 * (sr18 + 8)) /
1249              (((sr19 & 0x3f) + 2) * (1 << ((sr19 & 0xc0) >> 6)))) >> 12;
1250        pxclkdiv = ((gr0f & 0x08) >> 3 | (gr0f & 0x40) >> 5) + 1;
1251        tmp = sr0d & 0x06;
1252        vclkdiv = tmp == 0 ? 2 : tmp == 2 ? 4 : tmp == 4 ? 8 : 3; // * 2 !
1253        modedb[0].pxclk = ((100000000 * pxclkdiv * vclkdiv) >> 1) / fi;
1254
1255        if (verbosity > 0)
1256                output("detected startup mode: "
1257                       "fbset -g %d %d %d ??? %d -t %d %d %d %d %d %d %d\n",
1258                       modedb[0].xres, modedb[0].yres, modedb[0].xres,
1259                       modedb[0].bpp, modedb[0].pxclk, modedb[0].left_margin,
1260                       modedb[0].right_margin, modedb[0].upper_margin,
1261                       modedb[0].lower_margin, modedb[0].hsync_len,
1262                       modedb[0].vsync_len);
1263
1264        //
1265        // We use this goto target in case of a failed check_var. No, I really
1266        // do not want to do it in another way!
1267        //
1268
1269      tryagain:
1270
1271        i = (mode == NULL) ? 0 :
1272            !strncmp(mode, "640x480", 7) ? 1 :
1273            !strncmp(mode, "800x600", 7) ? 2 :
1274            !strncmp(mode, "1024x768", 8) ? 3 :
1275            !strncmp(mode, "1280x1024", 9) ? 4 : 0;
1276
1277        ref = (ref < 50) ? 50 : (ref > 85) ? 85 : ref;
1278
1279        if (i == 0) {
1280                info->var.pixclock = modedb[i].pxclk;
1281                info->var.bits_per_pixel = modedb[i].bpp;
1282        } else {
1283                info->var.pixclock = (100000000 /
1284                                      ((modedb[i].left_margin +
1285                                        modedb[i].xres +
1286                                        modedb[i].right_margin +
1287                                        modedb[i].hsync_len) *
1288                                       (modedb[i].upper_margin +
1289                                        modedb[i].yres +
1290                                        modedb[i].lower_margin +
1291                                        modedb[i].vsync_len) * ref / 10000));
1292                info->var.bits_per_pixel = bpp;
1293        }
1294
1295        info->var.left_margin = modedb[i].left_margin;
1296        info->var.right_margin = modedb[i].right_margin;
1297        info->var.xres = modedb[i].xres;
1298        if (!(modedb[i].yres == 1280 && modedb[i].bpp == 32))
1299                info->var.xres_virtual = modedb[i].vxres;
1300        else
1301                info->var.xres_virtual = modedb[i].xres;
1302        info->var.xoffset = 0;
1303        info->var.hsync_len = modedb[i].hsync_len;
1304        info->var.upper_margin = modedb[i].upper_margin;
1305        info->var.yres = modedb[i].yres;
1306        info->var.yres_virtual = modedb[i].vyres;
1307        info->var.yoffset = 0;
1308        info->var.lower_margin = modedb[i].lower_margin;
1309        info->var.vsync_len = modedb[i].vsync_len;
1310        info->var.sync = 0;
1311        info->var.vmode = FB_VMODE_NONINTERLACED;
1312
1313        if (cyblafb_check_var(&info->var, info)) {
1314                // 640x480 - 8@75 should really never fail. One case would
1315                // be fp == 1 and nativex < 640 ... give up then
1316                if (i == 1 && bpp == 8 && ref == 75) {
1317                        output("Can't find a valid mode :-(\n");
1318                        return -EINVAL;
1319                }
1320                // Our detected mode is unlikely to fail. If it does,
1321                // try 640x480 - 8@75 ...
1322                if (i == 0) {
1323                        mode = "640x480";
1324                        bpp = 8;
1325                        ref = 75;
1326                        output("Detected mode failed check_var! "
1327                               "Trying 640x480 - 8@75\n");
1328                        goto tryagain;
1329                }
1330                // A specified video mode failed for some reason.
1331                // Try the startup mode first
1332                output("Specified mode '%s' failed check! "
1333                       "Falling back to startup mode.\n", mode);
1334                mode = NULL;
1335                goto tryagain;
1336        }
1337
1338        return 0;
1339}
1340
1341//========================================================
1342//
1343// Detect activated memory size. Undefined values require
1344// memsize parameter.
1345//
1346//========================================================
1347
1348static unsigned int __devinit get_memsize(void)
1349{
1350        unsigned char tmp;
1351        unsigned int k;
1352
1353        if (memsize)
1354                k = memsize * Kb;
1355        else {
1356                tmp = read3X4(CR1F) & 0x0F;
1357                switch (tmp) {
1358                case 0x03:
1359                        k = 1 * 1024 * 1024;
1360                        break;
1361                case 0x07:
1362                        k = 2 * 1024 * 1024;
1363                        break;
1364                case 0x0F:
1365                        k = 4 * 1024 * 1024;
1366                        break;
1367                case 0x04:
1368                        k = 8 * 1024 * 1024;
1369                        break;
1370                default:
1371                        k = 1 * 1024 * 1024;
1372                        output("Unknown memory size code %x in CR1F."
1373                               " We default to 1 Mb for now, please"
1374                               " do provide a memsize parameter!\n", tmp);
1375                }
1376        }
1377
1378        if (verbosity > 0)
1379                output("framebuffer size = %d Kb\n", k / Kb);
1380        return k;
1381}
1382
1383//=========================================================
1384//
1385// Detect if a flat panel monitor connected to the special
1386// interface is active. Override is possible by fp and crt
1387// parameters.
1388//
1389//=========================================================
1390
1391static unsigned int __devinit get_displaytype(void)
1392{
1393        if (fp)
1394                return DISPLAY_FP;
1395        if (crt)
1396                return DISPLAY_CRT;
1397        return (read3CE(GR33) & 0x10) ? DISPLAY_FP : DISPLAY_CRT;
1398}
1399
1400//=====================================
1401//
1402// Get native resolution of flat panel
1403//
1404//=====================================
1405
1406static int __devinit get_nativex(void)
1407{
1408        int x, y, tmp;
1409
1410        if (nativex)
1411                return nativex;
1412
1413        tmp = (read3CE(GR52) >> 4) & 3;
1414
1415        switch (tmp) {
1416        case 0: x = 1280; y = 1024;
1417                break;
1418        case 2: x = 1024; y = 768;
1419                break;
1420        case 3: x = 800;  y = 600;
1421                break;
1422        case 4: x = 1400; y = 1050;
1423                break;
1424        case 1:
1425        default:
1426                x = 640; y = 480;
1427                break;
1428        }
1429
1430        if (verbosity > 0)
1431                output("%dx%d flat panel found\n", x, y);
1432        return x;
1433}
1434
1435static int __devinit cybla_pci_probe(struct pci_dev *dev,
1436                                     const struct pci_device_id *id)
1437{
1438        struct fb_info *info;
1439        struct cyblafb_par *par;
1440
1441        info = framebuffer_alloc(sizeof(struct cyblafb_par), &dev->dev);
1442        if (!info)
1443                goto errout_alloc_info;
1444
1445        info->pixmap.addr = kzalloc(CYBLAFB_PIXMAPSIZE, GFP_KERNEL);
1446        if (!info->pixmap.addr) {
1447                output("allocation of pixmap buffer failed!\n");
1448                goto errout_alloc_pixmap;
1449        }
1450        info->pixmap.size = CYBLAFB_PIXMAPSIZE - 4;
1451        info->pixmap.buf_align = 4;
1452        info->pixmap.access_align = 32;
1453        info->pixmap.flags = FB_PIXMAP_SYSTEM;
1454        info->pixmap.scan_align = 4;
1455
1456        par = info->par;
1457        par->ops = cyblafb_ops;
1458
1459        info->fix = cyblafb_fix;
1460        info->fbops = &par->ops;
1461        info->fix = cyblafb_fix;
1462
1463        if (pci_enable_device(dev)) {
1464                output("could not enable device!\n");
1465                goto errout_enable;
1466        }
1467        // might already be requested by vga console or vesafb,
1468        // so we do care about success
1469        if (!request_region(0x3c0, 0x20, "cyblafb")) {
1470                output("region 0x3c0/0x20 already reserved\n");
1471                vesafb |= 1;
1472
1473        }
1474        //
1475        // Graphics Engine Registers
1476        //
1477        if (!request_region(GEBase, 0x100, "cyblafb")) {
1478                output("region %#x/0x100 already reserved\n", GEBase);
1479                vesafb |= 2;
1480        }
1481
1482        regdump(par);
1483
1484        enable_mmio();
1485
1486        // setup MMIO region
1487        info->fix.mmio_start = pci_resource_start(dev, 1);
1488        info->fix.mmio_len = 0x20000;
1489
1490        if (!request_mem_region(info->fix.mmio_start,
1491                                info->fix.mmio_len, "cyblafb")) {
1492                output("request_mem_region failed for mmio region!\n");
1493                goto errout_mmio_reqmem;
1494        }
1495
1496        io_virt = ioremap_nocache(info->fix.mmio_start, info->fix.mmio_len);
1497
1498        if (!io_virt) {
1499                output("ioremap failed for mmio region\n");
1500                goto errout_mmio_remap;
1501        }
1502        // setup framebuffer memory ... might already be requested
1503        // by vesafb. Not to fail in case of an unsuccessful request
1504        // is useful if both are loaded.
1505        info->fix.smem_start = pci_resource_start(dev, 0);
1506        info->fix.smem_len = get_memsize();
1507
1508        if (!request_mem_region(info->fix.smem_start,
1509                                info->fix.smem_len, "cyblafb")) {
1510                output("region %#lx/%#x already reserved\n",
1511                       info->fix.smem_start, info->fix.smem_len);
1512                vesafb |= 4;
1513        }
1514
1515        info->screen_base = ioremap_nocache(info->fix.smem_start,
1516                                            info->fix.smem_len);
1517
1518        if (!info->screen_base) {
1519                output("ioremap failed for smem region\n");
1520                goto errout_smem_remap;
1521        }
1522
1523        displaytype = get_displaytype();
1524
1525        if (displaytype == DISPLAY_FP)
1526                nativex = get_nativex();
1527
1528        info->flags = FBINFO_DEFAULT
1529                    | FBINFO_HWACCEL_COPYAREA
1530                    | FBINFO_HWACCEL_FILLRECT
1531                    | FBINFO_HWACCEL_IMAGEBLIT
1532                    | FBINFO_READS_FAST
1533//                  | FBINFO_PARTIAL_PAN_OK
1534                    | FBINFO_MISC_ALWAYS_SETPAR;
1535
1536        info->pseudo_palette = par->pseudo_pal;
1537
1538        if (getstartupmode(info))
1539                goto errout_findmode;
1540
1541        fb_alloc_cmap(&info->cmap, 256, 0);
1542
1543        if (register_framebuffer(info)) {
1544                output("Could not register CyBla framebuffer\n");
1545                goto errout_register;
1546        }
1547
1548        pci_set_drvdata(dev, info);
1549
1550        //
1551        // normal exit and error paths
1552        //
1553
1554        return 0;
1555
1556      errout_register:
1557      errout_findmode:
1558        iounmap(info->screen_base);
1559      errout_smem_remap:
1560        if (!(vesafb & 4))
1561                release_mem_region(info->fix.smem_start, info->fix.smem_len);
1562        iounmap(io_virt);
1563      errout_mmio_remap:
1564        release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
1565      errout_mmio_reqmem:
1566        if (!(vesafb & 1))
1567                release_region(0x3c0, 32);
1568      errout_enable:
1569        kfree(info->pixmap.addr);
1570      errout_alloc_pixmap:
1571        framebuffer_release(info);
1572      errout_alloc_info:
1573        output("CyblaFB version %s aborting init.\n", VERSION);
1574        return -ENODEV;
1575}
1576
1577static void __devexit cybla_pci_remove(struct pci_dev *dev)
1578{
1579        struct fb_info *info = pci_get_drvdata(dev);
1580
1581        unregister_framebuffer(info);
1582        iounmap(io_virt);
1583        iounmap(info->screen_base);
1584        if (!(vesafb & 4))
1585                release_mem_region(info->fix.smem_start, info->fix.smem_len);
1586        release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
1587        fb_dealloc_cmap(&info->cmap);
1588        if (!(vesafb & 2))
1589                release_region(GEBase, 0x100);
1590        if (!(vesafb & 1))
1591                release_region(0x3c0, 32);
1592        kfree(info->pixmap.addr);
1593        framebuffer_release(info);
1594        output("CyblaFB version %s normal exit.\n", VERSION);
1595}
1596
1597//
1598// List of boards that we are trying to support
1599//
1600static struct pci_device_id cybla_devices[] = {
1601        {PCI_VENDOR_ID_TRIDENT, CYBERBLADEi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1602        {0,}
1603};
1604
1605MODULE_DEVICE_TABLE(pci, cybla_devices);
1606
1607static struct pci_driver cyblafb_pci_driver = {
1608        .name = "cyblafb",
1609        .id_table = cybla_devices,
1610        .probe = cybla_pci_probe,
1611        .remove = __devexit_p(cybla_pci_remove)
1612};
1613
1614//=============================================================
1615//
1616// kernel command line example:
1617//
1618//      video=cyblafb:1280x1024, bpp=16, ref=50 ...
1619//
1620// modprobe command line example:
1621//
1622//      modprobe cyblafb mode=1280x1024 bpp=16 ref=50 ...
1623//
1624//=============================================================
1625
1626static int __devinit cyblafb_init(void)
1627{
1628#ifndef MODULE
1629        char *options = NULL;
1630        char *opt;
1631
1632        if (fb_get_options("cyblafb", &options))
1633                return -ENODEV;
1634
1635        if (options && *options)
1636                while ((opt = strsep(&options, ",")) != NULL) {
1637                        if (!*opt)
1638                                continue;
1639                        else if (!strncmp(opt, "bpp=", 4))
1640                                bpp = simple_strtoul(opt + 4, NULL, 0);
1641                        else if (!strncmp(opt, "ref=", 4))
1642                                ref = simple_strtoul(opt + 4, NULL, 0);
1643                        else if (!strncmp(opt, "fp", 2))
1644                                displaytype = DISPLAY_FP;
1645                        else if (!strncmp(opt, "crt", 3))
1646                                displaytype = DISPLAY_CRT;
1647                        else if (!strncmp(opt, "nativex=", 8))
1648                                nativex = simple_strtoul(opt + 8, NULL, 0);
1649                        else if (!strncmp(opt, "center", 6))
1650                                center = 1;
1651                        else if (!strncmp(opt, "stretch", 7))
1652                                stretch = 1;
1653                        else if (!strncmp(opt, "pciwb=", 6))
1654                                pciwb = simple_strtoul(opt + 6, NULL, 0);
1655                        else if (!strncmp(opt, "pcirb=", 6))
1656                                pcirb = simple_strtoul(opt + 6, NULL, 0);
1657                        else if (!strncmp(opt, "pciwr=", 6))
1658                                pciwr = simple_strtoul(opt + 6, NULL, 0);
1659                        else if (!strncmp(opt, "pcirr=", 6))
1660                                pcirr = simple_strtoul(opt + 6, NULL, 0);
1661                        else if (!strncmp(opt, "memsize=", 8))
1662                                memsize = simple_strtoul(opt + 8, NULL, 0);
1663                        else if (!strncmp(opt, "verbosity=", 10))
1664                                verbosity = simple_strtoul(opt + 10, NULL, 0);
1665                        else
1666                                mode = opt;
1667                }
1668#endif
1669        output("CyblaFB version %s initializing\n", VERSION);
1670        return pci_register_driver(&cyblafb_pci_driver);
1671}
1672
1673static void __exit cyblafb_exit(void)
1674{
1675        pci_unregister_driver(&cyblafb_pci_driver);
1676}
1677
1678module_init(cyblafb_init);
1679module_exit(cyblafb_exit);
1680
1681MODULE_AUTHOR("Knut Petersen <knut_petersen@t-online.de>");
1682MODULE_DESCRIPTION("Framebuffer driver for Cyberblade/i1 graphics core");
1683MODULE_LICENSE("GPL");
1684