linux-old/drivers/video/cyber2000fb.c
<<
>>
Prefs
   1/*
   2 *  linux/drivers/video/cyber2000fb.c
   3 *
   4 *  Copyright (C) 1998-2000 Russell King
   5 *
   6 *  MIPS and 50xx clock support
   7 *  Copyright (C) 2001 Bradley D. LaRonde <brad@ltc.com>
   8 *
   9 *  32 bit support, text color and panning fixes for modes != 8 bit
  10 *  Copyright (C) 2002 Denis Oliver Kropp <dok@directfb.org>
  11 *
  12 * This program is free software; you can redistribute it and/or modify
  13 * it under the terms of the GNU General Public License version 2 as
  14 * published by the Free Software Foundation.
  15 *
  16 * Intergraphics CyberPro 2000, 2010 and 5000 frame buffer device
  17 *
  18 * Based on cyberfb.c.
  19 *
  20 * Note that we now use the new fbcon fix, var and cmap scheme.  We do still
  21 * have to check which console is the currently displayed one however, since
  22 * especially for the colourmap stuff.  Once fbcon has been fully migrated,
  23 * we can kill the last 5 references to cfb->currcon.
  24 *
  25 * We also use the new hotplug PCI subsystem.  I'm not sure if there are any
  26 * such cards, but I'm erring on the side of caution.  We don't want to go
  27 * pop just because someone does have one.
  28 *
  29 * Note that this doesn't work fully in the case of multiple CyberPro cards
  30 * with grabbers.  We currently can only attach to the first CyberPro card
  31 * found.
  32 */
  33#include <linux/config.h>
  34#include <linux/module.h>
  35#include <linux/kernel.h>
  36#include <linux/errno.h>
  37#include <linux/string.h>
  38#include <linux/mm.h>
  39#include <linux/tty.h>
  40#include <linux/slab.h>
  41#include <linux/delay.h>
  42#include <linux/fb.h>
  43#include <linux/pci.h>
  44#include <linux/init.h>
  45
  46#include <asm/io.h>
  47#include <asm/irq.h>
  48#include <asm/pgtable.h>
  49#include <asm/system.h>
  50#include <asm/uaccess.h>
  51
  52#include <video/fbcon.h>
  53#include <video/fbcon-cfb8.h>
  54#include <video/fbcon-cfb16.h>
  55#include <video/fbcon-cfb24.h>
  56#include <video/fbcon-cfb32.h>
  57
  58/*
  59 * Define this if you don't want RGB565, but RGB555 for 16bpp displays.
  60 */
  61/*#define CFB16_IS_CFB15*/
  62
  63#include "cyber2000fb.h"
  64
  65struct cfb_info {
  66        struct fb_info          fb;
  67        struct display_switch   *dispsw;
  68        struct pci_dev          *dev;
  69        unsigned char           *region;
  70        unsigned char           *regs;
  71        signed int              currcon;
  72        int                     func_use_count;
  73        u_long                  ref_ps;
  74
  75        /*
  76         * Clock divisors
  77         */
  78        u_int                   divisors[4];
  79
  80        struct {
  81                u8 red, green, blue;
  82        } palette[NR_PALETTE];
  83
  84        u_char                  mem_ctl1;
  85        u_char                  mem_ctl2;
  86        u_char                  mclk_mult;
  87        u_char                  mclk_div;
  88};
  89
  90static char default_font_storage[40];
  91static char *default_font = "Acorn8x8";
  92MODULE_PARM(default_font, "s");
  93MODULE_PARM_DESC(default_font, "Default font name");
  94
  95/*
  96 * Our access methods.
  97 */
  98#define cyber2000fb_writel(val,reg,cfb) writel(val, (cfb)->regs + (reg))
  99#define cyber2000fb_writew(val,reg,cfb) writew(val, (cfb)->regs + (reg))
 100#define cyber2000fb_writeb(val,reg,cfb) writeb(val, (cfb)->regs + (reg))
 101
 102#define cyber2000fb_readb(reg,cfb)      readb((cfb)->regs + (reg))
 103
 104static inline void
 105cyber2000_crtcw(unsigned int reg, unsigned int val, struct cfb_info *cfb)
 106{
 107        cyber2000fb_writew((reg & 255) | val << 8, 0x3d4, cfb);
 108}
 109
 110static inline void
 111cyber2000_grphw(unsigned int reg, unsigned int val, struct cfb_info *cfb)
 112{
 113        cyber2000fb_writew((reg & 255) | val << 8, 0x3ce, cfb);
 114}
 115
 116static inline unsigned int
 117cyber2000_grphr(unsigned int reg, struct cfb_info *cfb)
 118{
 119        cyber2000fb_writeb(reg, 0x3ce, cfb);
 120        return cyber2000fb_readb(0x3cf, cfb);
 121}
 122
 123static inline void
 124cyber2000_attrw(unsigned int reg, unsigned int val, struct cfb_info *cfb)
 125{
 126        cyber2000fb_readb(0x3da, cfb);
 127        cyber2000fb_writeb(reg, 0x3c0, cfb);
 128        cyber2000fb_readb(0x3c1, cfb);
 129        cyber2000fb_writeb(val, 0x3c0, cfb);
 130}
 131
 132static inline void
 133cyber2000_seqw(unsigned int reg, unsigned int val, struct cfb_info *cfb)
 134{
 135        cyber2000fb_writew((reg & 255) | val << 8, 0x3c4, cfb);
 136}
 137
 138/* -------------------- Hardware specific routines ------------------------- */
 139
 140/*
 141 * Hardware Cyber2000 Acceleration
 142 */
 143static void cyber2000_accel_wait(struct cfb_info *cfb)
 144{
 145        int count = 100000;
 146
 147        while (cyber2000fb_readb(CO_REG_CONTROL, cfb) & 0x80) {
 148                if (!count--) {
 149                        debug_printf("accel_wait timed out\n");
 150                        cyber2000fb_writeb(0, CO_REG_CONTROL, cfb);
 151                        return;
 152                }
 153                udelay(1);
 154        }
 155}
 156
 157static void cyber2000_accel_setup(struct display *p)
 158{
 159        struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
 160
 161        cfb->dispsw->setup(p);
 162}
 163
 164static void
 165cyber2000_accel_bmove(struct display *p, int sy, int sx, int dy, int dx,
 166                      int height, int width)
 167{
 168        struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
 169        struct fb_var_screeninfo *var = &p->fb_info->var;
 170        u_long src, dst;
 171        u_int fh, fw;
 172        int cmd = CO_CMD_L_PATTERN_FGCOL;
 173
 174        fw    = fontwidth(p);
 175        sx    *= fw;
 176        dx    *= fw;
 177        width *= fw;
 178        width -= 1;
 179
 180        if (sx < dx) {
 181                sx += width;
 182                dx += width;
 183                cmd |= CO_CMD_L_INC_LEFT;
 184        }
 185
 186        fh     = fontheight(p);
 187        sy     *= fh;
 188        dy     *= fh;
 189        height *= fh;
 190        height -= 1;
 191
 192        if (sy < dy) {
 193                sy += height;
 194                dy += height;
 195                cmd |= CO_CMD_L_INC_UP;
 196        }
 197
 198        src    = sx + sy * var->xres_virtual;
 199        dst    = dx + dy * var->xres_virtual;
 200
 201        cyber2000_accel_wait(cfb);
 202        cyber2000fb_writeb(0x00,  CO_REG_CONTROL, cfb);
 203        cyber2000fb_writeb(0x03,  CO_REG_FORE_MIX, cfb);
 204        cyber2000fb_writew(width, CO_REG_WIDTH, cfb);
 205
 206        if (var->bits_per_pixel != 24) {
 207                cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);
 208                cyber2000fb_writel(src, CO_REG_SRC_PTR, cfb);
 209        } else {
 210                cyber2000fb_writel(dst * 3, CO_REG_DEST_PTR, cfb);
 211                cyber2000fb_writeb(dst,     CO_REG_X_PHASE, cfb);
 212                cyber2000fb_writel(src * 3, CO_REG_SRC_PTR, cfb);
 213        }
 214
 215        cyber2000fb_writew(height, CO_REG_HEIGHT, cfb);
 216        cyber2000fb_writew(cmd,    CO_REG_CMD_L, cfb);
 217        cyber2000fb_writew(0x2800, CO_REG_CMD_H, cfb);
 218}
 219
 220static void
 221cyber2000_accel_clear(struct vc_data *conp, struct display *p, int sy, int sx,
 222                      int height, int width)
 223{
 224        struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
 225        struct fb_var_screeninfo *var = &p->fb_info->var;
 226        u_long dst;
 227        u_int fw, fh;
 228        u32 bgx = attr_bgcol_ec(p, conp);
 229
 230        fw = fontwidth(p);
 231        fh = fontheight(p);
 232
 233        dst    = sx * fw + sy * var->xres_virtual * fh;
 234        width  = width * fw - 1;
 235        height = height * fh - 1;
 236
 237        cyber2000_accel_wait(cfb);
 238        cyber2000fb_writeb(0x00,   CO_REG_CONTROL,  cfb);
 239        cyber2000fb_writeb(0x03,   CO_REG_FORE_MIX, cfb);
 240        cyber2000fb_writew(width,  CO_REG_WIDTH,    cfb);
 241        cyber2000fb_writew(height, CO_REG_HEIGHT,   cfb);
 242
 243        switch (var->bits_per_pixel) {
 244        case 15:
 245        case 16:
 246                bgx = ((u16 *)p->dispsw_data)[bgx];
 247        case 8:
 248                cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);
 249                break;
 250
 251        case 24:
 252                cyber2000fb_writel(dst * 3, CO_REG_DEST_PTR, cfb);
 253                cyber2000fb_writeb(dst, CO_REG_X_PHASE, cfb);
 254                bgx = ((u32 *)p->dispsw_data)[bgx];
 255                break;
 256
 257        case 32:
 258                bgx = ((u32 *)p->dispsw_data)[bgx];
 259                cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);
 260                break;
 261        }
 262
 263        cyber2000fb_writel(bgx, CO_REG_FOREGROUND, cfb);
 264        cyber2000fb_writew(CO_CMD_L_PATTERN_FGCOL, CO_REG_CMD_L, cfb);
 265        cyber2000fb_writew(0x0800, CO_REG_CMD_H, cfb);
 266}
 267
 268static void
 269cyber2000_accel_putc(struct vc_data *conp, struct display *p, int c,
 270                     int yy, int xx)
 271{
 272        struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
 273
 274        cyber2000_accel_wait(cfb);
 275        cfb->dispsw->putc(conp, p, c, yy, xx);
 276}
 277
 278static void
 279cyber2000_accel_putcs(struct vc_data *conp, struct display *p,
 280                      const unsigned short *s, int count, int yy, int xx)
 281{
 282        struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
 283
 284        cyber2000_accel_wait(cfb);
 285        cfb->dispsw->putcs(conp, p, s, count, yy, xx);
 286}
 287
 288static void cyber2000_accel_revc(struct display *p, int xx, int yy)
 289{
 290        struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
 291
 292        cyber2000_accel_wait(cfb);
 293        cfb->dispsw->revc(p, xx, yy);
 294}
 295
 296static void
 297cyber2000_accel_clear_margins(struct vc_data *conp, struct display *p,
 298                              int bottom_only)
 299{
 300        struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
 301
 302        cfb->dispsw->clear_margins(conp, p, bottom_only);
 303}
 304
 305static struct display_switch fbcon_cyber_accel = {
 306        setup:          cyber2000_accel_setup,
 307        bmove:          cyber2000_accel_bmove,
 308        clear:          cyber2000_accel_clear,
 309        putc:           cyber2000_accel_putc,
 310        putcs:          cyber2000_accel_putcs,
 311        revc:           cyber2000_accel_revc,
 312        clear_margins:  cyber2000_accel_clear_margins,
 313        fontwidthmask:  FONTWIDTH(8)|FONTWIDTH(16)
 314};
 315
 316/*
 317 *    Set a single color register. Return != 0 for invalid regno.
 318 */
 319static int
 320cyber2000_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
 321                    u_int transp, struct fb_info *info)
 322{
 323        struct cfb_info *cfb = (struct cfb_info *)info;
 324
 325        u_int alpha = transp ^ 0xFFFF;
 326
 327        if (regno >= NR_PALETTE)
 328                return 1;
 329
 330        red   >>= 8;
 331        green >>= 8;
 332        blue  >>= 8;
 333        alpha >>= 8;
 334
 335        cfb->palette[regno].red   = red;
 336        cfb->palette[regno].green = green;
 337        cfb->palette[regno].blue  = blue;
 338
 339        switch (cfb->fb.var.bits_per_pixel) {
 340#ifdef FBCON_HAS_CFB8
 341        case 8:
 342                cyber2000fb_writeb(regno, 0x3c8, cfb);
 343                cyber2000fb_writeb(red,   0x3c9, cfb);
 344                cyber2000fb_writeb(green, 0x3c9, cfb);
 345                cyber2000fb_writeb(blue,  0x3c9, cfb);
 346                break;
 347#endif
 348
 349#ifdef FBCON_HAS_CFB16
 350        case 16:
 351#ifndef CFB16_IS_CFB15
 352                if (regno < 64) {
 353                        /* write green */
 354                        cyber2000fb_writeb(regno << 2, 0x3c8, cfb);
 355                        cyber2000fb_writeb(cfb->palette[regno >> 1].red, 0x3c9, cfb);
 356                        cyber2000fb_writeb(green, 0x3c9, cfb);
 357                        cyber2000fb_writeb(cfb->palette[regno >> 1].blue, 0x3c9, cfb);
 358                }
 359
 360                if (regno < 32) {
 361                        /* write red,blue */
 362                        cyber2000fb_writeb(regno << 3, 0x3c8, cfb);
 363                        cyber2000fb_writeb(red, 0x3c9, cfb);
 364                        cyber2000fb_writeb(cfb->palette[regno << 1].green, 0x3c9, cfb);
 365                        cyber2000fb_writeb(blue, 0x3c9, cfb);
 366                }
 367
 368                if (regno < 16)
 369                        ((u16 *)cfb->fb.pseudo_palette)[regno] =
 370                                ((red   << 8) & 0xf800) |
 371                                ((green << 3) & 0x07e0) |
 372                                ((blue  >> 3));
 373                break;
 374#endif
 375
 376        case 15:
 377                if (regno < 32) {
 378                        cyber2000fb_writeb(regno << 3, 0x3c8, cfb);
 379                        cyber2000fb_writeb(red, 0x3c9, cfb);
 380                        cyber2000fb_writeb(green, 0x3c9, cfb);
 381                        cyber2000fb_writeb(blue, 0x3c9, cfb);
 382                }
 383                if (regno < 16)
 384                        ((u16 *)cfb->fb.pseudo_palette)[regno] =
 385                                ((red   << 7) & 0x7c00) |
 386                                ((green << 2) & 0x03e0) |
 387                                ((blue  >> 3));
 388                break;
 389
 390#endif
 391
 392#ifdef FBCON_HAS_CFB24
 393        case 24:
 394                cyber2000fb_writeb(regno, 0x3c8, cfb);
 395                cyber2000fb_writeb(red,   0x3c9, cfb);
 396                cyber2000fb_writeb(green, 0x3c9, cfb);
 397                cyber2000fb_writeb(blue,  0x3c9, cfb);
 398
 399                if (regno < 16)
 400                        ((u32 *)cfb->fb.pseudo_palette)[regno] =
 401                                (red << 16) | (green << 8) | blue;
 402                break;
 403#endif
 404
 405#ifdef FBCON_HAS_CFB32
 406        case 32:
 407                cyber2000fb_writeb(regno, 0x3c8, cfb);
 408                cyber2000fb_writeb(red,   0x3c9, cfb);
 409                cyber2000fb_writeb(green, 0x3c9, cfb);
 410                cyber2000fb_writeb(blue,  0x3c9, cfb);
 411
 412                if (regno < 16)
 413                        ((u32 *)cfb->fb.pseudo_palette)[regno] =
 414                                (alpha << 24) | (red << 16) | (green << 8) | blue;
 415                break;
 416#endif
 417
 418        default:
 419                return 1;
 420        }
 421
 422        return 0;
 423}
 424
 425struct par_info {
 426        /*
 427         * Hardware
 428         */
 429        u_char  clock_mult;
 430        u_char  clock_div;
 431        u_char  visualid;
 432        u_char  pixformat;
 433        u_char  crtc_ofl;
 434        u_char  crtc[19];
 435        u_int   width;
 436        u_int   pitch;
 437        u_int   fetch;
 438
 439        /*
 440         * Other
 441         */
 442        u_char  palette_ctrl;
 443        u_int   vmode;
 444};
 445
 446static const u_char crtc_idx[] = {
 447        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
 448        0x08, 0x09,
 449        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18
 450};
 451
 452static void cyber2000fb_set_timing(struct cfb_info *cfb, struct par_info *hw)
 453{
 454        u_int i;
 455
 456        /*
 457         * Blank palette
 458         */
 459        for (i = 0; i < NR_PALETTE; i++) {
 460                cyber2000fb_writeb(i, 0x3c8, cfb);
 461                cyber2000fb_writeb(0, 0x3c9, cfb);
 462                cyber2000fb_writeb(0, 0x3c9, cfb);
 463                cyber2000fb_writeb(0, 0x3c9, cfb);
 464        }
 465
 466        cyber2000fb_writeb(0xef, 0x3c2, cfb);
 467        cyber2000_crtcw(0x11, 0x0b, cfb);
 468        cyber2000_attrw(0x11, 0x00, cfb);
 469
 470        cyber2000_seqw(0x00, 0x01, cfb);
 471        cyber2000_seqw(0x01, 0x01, cfb);
 472        cyber2000_seqw(0x02, 0x0f, cfb);
 473        cyber2000_seqw(0x03, 0x00, cfb);
 474        cyber2000_seqw(0x04, 0x0e, cfb);
 475        cyber2000_seqw(0x00, 0x03, cfb);
 476
 477        for (i = 0; i < sizeof(crtc_idx); i++)
 478                cyber2000_crtcw(crtc_idx[i], hw->crtc[i], cfb);
 479
 480        for (i = 0x0a; i < 0x10; i++)
 481                cyber2000_crtcw(i, 0, cfb);
 482
 483        cyber2000_grphw(0x11, hw->crtc_ofl, cfb);
 484        cyber2000_grphw(0x00, 0x00, cfb);
 485        cyber2000_grphw(0x01, 0x00, cfb);
 486        cyber2000_grphw(0x02, 0x00, cfb);
 487        cyber2000_grphw(0x03, 0x00, cfb);
 488        cyber2000_grphw(0x04, 0x00, cfb);
 489        cyber2000_grphw(0x05, 0x60, cfb);
 490        cyber2000_grphw(0x06, 0x05, cfb);
 491        cyber2000_grphw(0x07, 0x0f, cfb);
 492        cyber2000_grphw(0x08, 0xff, cfb);
 493
 494        /* Attribute controller registers */
 495        for (i = 0; i < 16; i++)
 496                cyber2000_attrw(i, i, cfb);
 497
 498        cyber2000_attrw(0x10, 0x01, cfb);
 499        cyber2000_attrw(0x11, 0x00, cfb);
 500        cyber2000_attrw(0x12, 0x0f, cfb);
 501        cyber2000_attrw(0x13, 0x00, cfb);
 502        cyber2000_attrw(0x14, 0x00, cfb);
 503
 504        /* woody: set the interlaced bit... */
 505        /* FIXME: what about doublescan? */
 506        cyber2000fb_writeb(0x11, 0x3ce, cfb);
 507        i = cyber2000fb_readb(0x3cf, cfb);
 508        if (hw->vmode == FB_VMODE_INTERLACED)
 509                i |= 0x20;
 510        else
 511                i &= ~0x20;
 512        cyber2000fb_writeb(i, 0x3cf, cfb);
 513
 514        /* PLL registers */
 515        cyber2000_grphw(DCLK_MULT, hw->clock_mult, cfb);
 516        cyber2000_grphw(DCLK_DIV,  hw->clock_div, cfb);
 517        cyber2000_grphw(MCLK_MULT, cfb->mclk_mult, cfb);
 518        cyber2000_grphw(MCLK_DIV,  cfb->mclk_div, cfb);
 519        cyber2000_grphw(0x90, 0x01, cfb);
 520        cyber2000_grphw(0xb9, 0x80, cfb);
 521        cyber2000_grphw(0xb9, 0x00, cfb);
 522
 523        cyber2000fb_writeb(0x56, 0x3ce, cfb);
 524        i = cyber2000fb_readb(0x3cf, cfb);
 525        cyber2000fb_writeb(i | 4, 0x3cf, cfb);
 526        cyber2000fb_writeb(hw->palette_ctrl, 0x3c6, cfb);
 527        cyber2000fb_writeb(i,    0x3cf, cfb);
 528
 529        cyber2000fb_writeb(0x20, 0x3c0, cfb);
 530        cyber2000fb_writeb(0xff, 0x3c6, cfb);
 531
 532        cyber2000_grphw(0x14, hw->fetch, cfb);
 533        cyber2000_grphw(0x15, ((hw->fetch >> 8) & 0x03) |
 534                              ((hw->pitch >> 4) & 0x30), cfb);
 535        cyber2000_grphw(0x77, hw->visualid, cfb);
 536
 537        /* make sure we stay in linear mode */
 538        cyber2000_grphw(0x33, 0x0d, cfb);
 539
 540        /*
 541         * Set up accelerator registers
 542         */
 543        cyber2000fb_writew(hw->width,     CO_REG_SRC_WIDTH,  cfb);
 544        cyber2000fb_writew(hw->width,     CO_REG_DEST_WIDTH, cfb);
 545        cyber2000fb_writeb(hw->pixformat, CO_REG_PIX_FORMAT, cfb);
 546}
 547
 548static inline int
 549cyber2000fb_update_start(struct cfb_info *cfb, struct fb_var_screeninfo *var)
 550{
 551        u_int base;
 552
 553        base = var->yoffset * var->xres_virtual + var->xoffset;
 554
 555        /* have to be careful, because bits_per_pixel might be 15
 556           in this version of the driver -- dok@directfb.org 2002/06/13 */
 557        base *= (var->bits_per_pixel + 7) >> 3;
 558
 559        base >>= 2;
 560
 561        if (base >= 1 << 20)
 562                return -EINVAL;
 563
 564        cyber2000_grphw(0x10, base >> 16 | 0x10, cfb);
 565        cyber2000_crtcw(0x0c, base >> 8, cfb);
 566        cyber2000_crtcw(0x0d, base, cfb);
 567
 568        return 0;
 569}
 570
 571/*
 572 * Set the Colormap
 573 */
 574static int
 575cyber2000fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
 576                     struct fb_info *info)
 577{
 578        struct cfb_info *cfb = (struct cfb_info *)info;
 579        struct fb_cmap *dcmap = &fb_display[con].cmap;
 580        int err = 0;
 581
 582        /* no colormap allocated? */
 583        if (!dcmap->len) {
 584                int size;
 585
 586                if (cfb->fb.var.bits_per_pixel == 16)
 587                        size = 32;
 588                else
 589                        size = 256;
 590
 591                err = fb_alloc_cmap(dcmap, size, 0);
 592        }
 593
 594        /*
 595         * we should be able to remove this test once fbcon has been
 596         * "improved" --rmk
 597         */
 598        if (!err && con == cfb->currcon) {
 599                err = fb_set_cmap(cmap, kspc, cyber2000_setcolreg, &cfb->fb);
 600                dcmap = &cfb->fb.cmap;
 601        }
 602
 603        if (!err)
 604                fb_copy_cmap(cmap, dcmap, kspc ? 0 : 1);
 605
 606        return err;
 607}
 608
 609static int
 610cyber2000fb_decode_crtc(struct par_info *hw, struct cfb_info *cfb,
 611                        struct fb_var_screeninfo *var)
 612{
 613        u_int Htotal, Hblankend, Hsyncend;
 614        u_int Vtotal, Vdispend, Vblankstart, Vblankend, Vsyncstart, Vsyncend;
 615#define BIT(v,b1,m,b2) (((v >> b1) & m) << b2)
 616
 617        hw->crtc[13] = hw->pitch;
 618        hw->crtc[17] = 0xe3;
 619        hw->crtc[14] = 0;
 620        hw->crtc[8]  = 0;
 621
 622        Htotal      = var->xres + var->right_margin +
 623                      var->hsync_len + var->left_margin;
 624
 625        if (Htotal > 2080)
 626                return -EINVAL;
 627
 628        hw->crtc[0] = (Htotal >> 3) - 5;
 629        hw->crtc[1] = (var->xres >> 3) - 1;
 630        hw->crtc[2] = var->xres >> 3;
 631        hw->crtc[4] = (var->xres + var->right_margin) >> 3;
 632
 633        Hblankend   = (Htotal - 4*8) >> 3;
 634
 635        hw->crtc[3] = BIT(Hblankend,  0, 0x1f,  0) |
 636                      BIT(1,          0, 0x01,  7);
 637
 638        Hsyncend    = (var->xres + var->right_margin + var->hsync_len) >> 3;
 639
 640        hw->crtc[5] = BIT(Hsyncend,   0, 0x1f,  0) |
 641                      BIT(Hblankend,  5, 0x01,  7);
 642
 643        Vdispend    = var->yres - 1;
 644        Vsyncstart  = var->yres + var->lower_margin;
 645        Vsyncend    = var->yres + var->lower_margin + var->vsync_len;
 646        Vtotal      = var->yres + var->lower_margin + var->vsync_len +
 647                      var->upper_margin - 2;
 648
 649        if (Vtotal > 2047)
 650                return -EINVAL;
 651
 652        Vblankstart = var->yres + 6;
 653        Vblankend   = Vtotal - 10;
 654
 655        hw->crtc[6]  = Vtotal;
 656        hw->crtc[7]  = BIT(Vtotal,     8, 0x01,  0) |
 657                        BIT(Vdispend,   8, 0x01,  1) |
 658                        BIT(Vsyncstart, 8, 0x01,  2) |
 659                        BIT(Vblankstart,8, 0x01,  3) |
 660                        BIT(1,          0, 0x01,  4) |
 661                        BIT(Vtotal,     9, 0x01,  5) |
 662                        BIT(Vdispend,   9, 0x01,  6) |
 663                        BIT(Vsyncstart, 9, 0x01,  7);
 664        hw->crtc[9]  = BIT(0,          0, 0x1f,  0) |
 665                        BIT(Vblankstart,9, 0x01,  5) |
 666                        BIT(1,          0, 0x01,  6);
 667        hw->crtc[10] = Vsyncstart;
 668        hw->crtc[11] = BIT(Vsyncend,   0, 0x0f,  0) |
 669                       BIT(1,          0, 0x01,  7);
 670        hw->crtc[12] = Vdispend;
 671        hw->crtc[15] = Vblankstart;
 672        hw->crtc[16] = Vblankend;
 673        hw->crtc[18] = 0xff;
 674
 675        /* overflow - graphics reg 0x11 */
 676        /* 0=VTOTAL:10 1=VDEND:10 2=VRSTART:10 3=VBSTART:10
 677         * 4=LINECOMP:10 5-IVIDEO 6=FIXCNT
 678         */
 679        hw->crtc_ofl =
 680                BIT(Vtotal,     10, 0x01,  0) |
 681                BIT(Vdispend,   10, 0x01,  1) |
 682                BIT(Vsyncstart, 10, 0x01,  2) |
 683                BIT(Vblankstart,10, 0x01,  3) |
 684                1 << 4;
 685
 686        return 0;
 687}
 688
 689/*
 690 * The following was discovered by a good monitor, bit twiddling, theorising
 691 * and but mostly luck.  Strangely, it looks like everyone elses' PLL!
 692 *
 693 * Clock registers:
 694 *   fclock = fpll / div2
 695 *   fpll   = fref * mult / div1
 696 * where:
 697 *   fref = 14.318MHz (69842ps)
 698 *   mult = reg0xb0.7:0
 699 *   div1 = (reg0xb1.5:0 + 1)
 700 *   div2 =  2^(reg0xb1.7:6)
 701 *   fpll should be between 115 and 260 MHz
 702 *  (8696ps and 3846ps)
 703 */
 704static int
 705cyber2000fb_decode_clock(struct par_info *hw, struct cfb_info *cfb,
 706                         struct fb_var_screeninfo *var)
 707{
 708        u_long pll_ps = var->pixclock;
 709        const u_long ref_ps = cfb->ref_ps;
 710        u_int div2, t_div1, best_div1, best_mult;
 711        int best_diff;
 712        int vco;
 713
 714        /*
 715         * Step 1:
 716         *   find div2 such that 115MHz < fpll < 260MHz
 717         *   and 0 <= div2 < 4
 718         */
 719        for (div2 = 0; div2 < 4; div2++) {
 720                u_long new_pll;
 721
 722                new_pll = pll_ps / cfb->divisors[div2];
 723                if (8696 > new_pll && new_pll > 3846) {
 724                        pll_ps = new_pll;
 725                        break;
 726                }
 727        }
 728
 729        if (div2 == 4)
 730                return -EINVAL;
 731
 732        /*
 733         * Step 2:
 734         *  Given pll_ps and ref_ps, find:
 735         *    pll_ps * 0.995 < pll_ps_calc < pll_ps * 1.005
 736         *  where { 1 < best_div1 < 32, 1 < best_mult < 256 }
 737         *    pll_ps_calc = best_div1 / (ref_ps * best_mult)
 738         */
 739        best_diff = 0x7fffffff;
 740        best_mult = 32;
 741        best_div1 = 255;
 742        for (t_div1 = 32; t_div1 > 1; t_div1 -= 1) {
 743                u_int rr, t_mult, t_pll_ps;
 744                int diff;
 745
 746                /*
 747                 * Find the multiplier for this divisor
 748                 */
 749                rr = ref_ps * t_div1;
 750                t_mult = (rr + pll_ps / 2) / pll_ps;
 751
 752                /*
 753                 * Is the multiplier within the correct range?
 754                 */
 755                if (t_mult > 256 || t_mult < 2)
 756                        continue;
 757
 758                /*
 759                 * Calculate the actual clock period from this multiplier
 760                 * and divisor, and estimate the error.
 761                 */
 762                t_pll_ps = (rr + t_mult / 2) / t_mult;
 763                diff = pll_ps - t_pll_ps;
 764                if (diff < 0)
 765                        diff = -diff;
 766
 767                if (diff < best_diff) {
 768                        best_diff = diff;
 769                        best_mult = t_mult;
 770                        best_div1 = t_div1;
 771                }
 772
 773                /*
 774                 * If we hit an exact value, there is no point in continuing.
 775                 */
 776                if (diff == 0)
 777                        break;
 778        }
 779
 780        /*
 781         * Step 3:
 782         *  combine values
 783         */
 784        hw->clock_mult = best_mult - 1;
 785        hw->clock_div  = div2 << 6 | (best_div1 - 1);
 786
 787        vco = ref_ps * best_div1 / best_mult;
 788        if ((ref_ps == 40690) && (vco < 5556))
 789                /* Set VFSEL when VCO > 180MHz (5.556 ps). */
 790                hw->clock_div |= DCLK_DIV_VFSEL;
 791
 792        return 0;
 793}
 794
 795/*
 796 * Decode the info required for the hardware.
 797 * This involves the PLL parameters for the dot clock,
 798 * CRTC registers, and accelerator settings.
 799 */
 800static int
 801cyber2000fb_decode_var(struct fb_var_screeninfo *var, struct cfb_info *cfb,
 802                       struct par_info *hw)
 803{
 804        int err;
 805
 806        hw->width = var->xres_virtual;
 807        hw->palette_ctrl = 0x06;
 808        hw->vmode = var->vmode;
 809
 810        switch (var->bits_per_pixel) {
 811#ifdef FBCON_HAS_CFB8
 812        case 8: /* PSEUDOCOLOUR, 256 */
 813                hw->pixformat           = PIXFORMAT_8BPP;
 814                hw->visualid            = VISUALID_256;
 815                hw->pitch               = hw->width >> 3;
 816                break;
 817#endif
 818#ifdef FBCON_HAS_CFB16
 819        case 16:/* DIRECTCOLOUR, 64k */
 820#ifndef CFB16_IS_CFB15
 821                hw->pixformat           = PIXFORMAT_16BPP;
 822                hw->visualid            = VISUALID_64K;
 823                hw->pitch               = hw->width >> 2;
 824                hw->palette_ctrl        |= 0x10;
 825                break;
 826#endif
 827        case 15:/* DIRECTCOLOUR, 32k */
 828                hw->pixformat           = PIXFORMAT_16BPP;
 829                hw->visualid            = VISUALID_32K;
 830                hw->pitch               = hw->width >> 2;
 831                hw->palette_ctrl        |= 0x10;
 832                break;
 833
 834#endif
 835#ifdef FBCON_HAS_CFB24
 836        case 24:/* TRUECOLOUR, 16m */
 837                hw->pixformat           = PIXFORMAT_24BPP;
 838                hw->visualid            = VISUALID_16M;
 839                hw->width               *= 3;
 840                hw->pitch               = hw->width >> 3;
 841                hw->palette_ctrl        |= 0x10;
 842                break;
 843#endif
 844#ifdef FBCON_HAS_CFB32
 845        case 32:/* TRUECOLOUR, 16m */
 846                hw->pixformat           = PIXFORMAT_32BPP;
 847                hw->visualid            = VISUALID_16M_32;
 848                hw->pitch               = hw->width >> 1;
 849                hw->palette_ctrl        |= 0x10;
 850                break;
 851#endif
 852        default:
 853                return -EINVAL;
 854        }
 855
 856        err = cyber2000fb_decode_clock(hw, cfb, var);
 857        if (err)
 858                return err;
 859
 860        err = cyber2000fb_decode_crtc(hw, cfb, var);
 861        if (err)
 862                return err;
 863
 864        hw->width -= 1;
 865        hw->fetch = hw->pitch;
 866        if (!(cfb->mem_ctl2 & MEM_CTL2_64BIT))
 867                hw->fetch <<= 1;
 868        hw->fetch += 1;
 869
 870        return 0;
 871}
 872
 873/*
 874 *    Set the User Defined Part of the Display
 875 */
 876static int
 877cyber2000fb_set_var(struct fb_var_screeninfo *var, int con,
 878                    struct fb_info *info)
 879{
 880        struct cfb_info *cfb = (struct cfb_info *)info;
 881        struct display *display;
 882        struct par_info hw;
 883        int err, chgvar = 0;
 884
 885        /*
 886         * CONUPDATE and SMOOTH_XPAN are equal.  However,
 887         * SMOOTH_XPAN is only used internally by fbcon.
 888         */
 889        if (var->vmode & FB_VMODE_CONUPDATE) {
 890                var->vmode |= FB_VMODE_YWRAP;
 891                var->xoffset = cfb->fb.var.xoffset;
 892                var->yoffset = cfb->fb.var.yoffset;
 893        }
 894
 895        err = cyber2000fb_decode_var(var, (struct cfb_info *)info, &hw);
 896        if (err)
 897                return err;
 898
 899        if (var->activate & FB_ACTIVATE_TEST)
 900                return 0;
 901
 902        if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
 903                return -EINVAL;
 904
 905        if (cfb->fb.var.xres != var->xres)
 906                chgvar = 1;
 907        if (cfb->fb.var.yres != var->yres)
 908                chgvar = 1;
 909        if (cfb->fb.var.xres_virtual != var->xres_virtual)
 910                chgvar = 1;
 911        if (cfb->fb.var.yres_virtual != var->yres_virtual)
 912                chgvar = 1;
 913        if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel)
 914                chgvar = 1;
 915
 916        if (con < 0) {
 917                display = cfb->fb.disp;
 918                chgvar = 0;
 919        } else {
 920                display = fb_display + con;
 921        }
 922
 923        var->red.msb_right      = 0;
 924        var->green.msb_right    = 0;
 925        var->blue.msb_right     = 0;
 926
 927        switch (var->bits_per_pixel) {
 928#ifdef FBCON_HAS_CFB8
 929        case 8: /* PSEUDOCOLOUR, 256 */
 930                var->red.offset         = 0;
 931                var->red.length         = 8;
 932                var->green.offset       = 0;
 933                var->green.length       = 8;
 934                var->blue.offset        = 0;
 935                var->blue.length        = 8;
 936
 937                cfb->fb.fix.visual      = FB_VISUAL_PSEUDOCOLOR;
 938                cfb->dispsw             = &fbcon_cfb8;
 939                display->dispsw_data    = NULL;
 940                display->next_line      = var->xres_virtual;
 941                break;
 942#endif
 943#ifdef FBCON_HAS_CFB16
 944        case 16:/* DIRECTCOLOUR, 64k */
 945#ifndef CFB16_IS_CFB15
 946                var->red.offset         = 11;
 947                var->red.length         = 5;
 948                var->green.offset       = 5;
 949                var->green.length       = 6;
 950                var->blue.offset        = 0;
 951                var->blue.length        = 5;
 952
 953                cfb->fb.fix.visual      = FB_VISUAL_DIRECTCOLOR;
 954                cfb->dispsw             = &fbcon_cfb16;
 955                display->dispsw_data    = cfb->fb.pseudo_palette;
 956                display->next_line      = var->xres_virtual * 2;
 957                break;
 958#endif
 959        case 15:/* DIRECTCOLOUR, 32k */
 960                var->bits_per_pixel     = 15;
 961                var->red.offset         = 10;
 962                var->red.length         = 5;
 963                var->green.offset       = 5;
 964                var->green.length       = 5;
 965                var->blue.offset        = 0;
 966                var->blue.length        = 5;
 967
 968                cfb->fb.fix.visual      = FB_VISUAL_DIRECTCOLOR;
 969                cfb->dispsw             = &fbcon_cfb16;
 970                display->dispsw_data    = cfb->fb.pseudo_palette;
 971                display->next_line      = var->xres_virtual * 2;
 972                break;
 973#endif
 974#ifdef FBCON_HAS_CFB24
 975        case 24:/* TRUECOLOUR, 16m */
 976                var->red.offset         = 16;
 977                var->red.length         = 8;
 978                var->green.offset       = 8;
 979                var->green.length       = 8;
 980                var->blue.offset        = 0;
 981                var->blue.length        = 8;
 982
 983                cfb->fb.fix.visual      = FB_VISUAL_TRUECOLOR;
 984                cfb->dispsw             = &fbcon_cfb24;
 985                display->dispsw_data    = cfb->fb.pseudo_palette;
 986                display->next_line      = var->xres_virtual * 3;
 987                break;
 988#endif
 989#ifdef FBCON_HAS_CFB32
 990        case 32:/* TRUECOLOUR, 16m */
 991                var->transp.offset      = 24;
 992                var->transp.length      = 8;
 993                var->red.offset         = 16;
 994                var->red.length         = 8;
 995                var->green.offset       = 8;
 996                var->green.length       = 8;
 997                var->blue.offset        = 0;
 998                var->blue.length        = 8;
 999
1000                cfb->fb.fix.visual      = FB_VISUAL_TRUECOLOR;
1001                cfb->dispsw             = &fbcon_cfb32;
1002                display->dispsw_data    = cfb->fb.pseudo_palette;
1003                display->next_line      = var->xres_virtual * 4;
1004                break;
1005#endif
1006        default:/* in theory this should never happen */
1007                printk(KERN_WARNING "%s: no support for %dbpp\n",
1008                       cfb->fb.fix.id, var->bits_per_pixel);
1009                cfb->dispsw = &fbcon_dummy;
1010                break;
1011        }
1012
1013        if (var->accel_flags & FB_ACCELF_TEXT && cfb->dispsw != &fbcon_dummy)
1014                display->dispsw = &fbcon_cyber_accel;
1015        else
1016                display->dispsw = cfb->dispsw;
1017
1018        cfb->fb.fix.line_length = display->next_line;
1019
1020        display->screen_base    = cfb->fb.screen_base;
1021        display->line_length    = cfb->fb.fix.line_length;
1022        display->visual         = cfb->fb.fix.visual;
1023        display->type           = cfb->fb.fix.type;
1024        display->type_aux       = cfb->fb.fix.type_aux;
1025        display->ypanstep       = cfb->fb.fix.ypanstep;
1026        display->ywrapstep      = cfb->fb.fix.ywrapstep;
1027        display->can_soft_blank = 1;
1028        display->inverse        = 0;
1029
1030        cfb->fb.var = *var;
1031        cfb->fb.var.activate &= ~FB_ACTIVATE_ALL;
1032
1033        /*
1034         * Update the old var.  The fbcon drivers still use this.
1035         * Once they are using cfb->fb.var, this can be dropped.
1036         *                                      --rmk
1037         */
1038        display->var = cfb->fb.var;
1039
1040        /*
1041         * If we are setting all the virtual consoles, also set the
1042         * defaults used to create new consoles.
1043         */
1044        if (var->activate & FB_ACTIVATE_ALL)
1045                cfb->fb.disp->var = cfb->fb.var;
1046
1047        if (chgvar && info && cfb->fb.changevar)
1048                cfb->fb.changevar(con);
1049
1050        cyber2000fb_update_start(cfb, var);
1051        cyber2000fb_set_timing(cfb, &hw);
1052        fb_set_cmap(&cfb->fb.cmap, 1, cyber2000_setcolreg, &cfb->fb);
1053
1054        return 0;
1055}
1056
1057
1058/*
1059 *    Pan or Wrap the Display
1060 */
1061static int
1062cyber2000fb_pan_display(struct fb_var_screeninfo *var, int con,
1063                        struct fb_info *info)
1064{
1065        struct cfb_info *cfb = (struct cfb_info *)info;
1066        u_int y_bottom;
1067
1068        y_bottom = var->yoffset;
1069
1070        if (!(var->vmode & FB_VMODE_YWRAP))
1071                y_bottom += var->yres;
1072
1073        if (var->xoffset > (var->xres_virtual - var->xres))
1074                return -EINVAL;
1075        if (y_bottom > cfb->fb.var.yres_virtual)
1076                return -EINVAL;
1077
1078        if (cyber2000fb_update_start(cfb, var))
1079                return -EINVAL;
1080
1081        cfb->fb.var.xoffset = var->xoffset;
1082        cfb->fb.var.yoffset = var->yoffset;
1083        if (var->vmode & FB_VMODE_YWRAP) {
1084                cfb->fb.var.vmode |= FB_VMODE_YWRAP;
1085        } else {
1086                cfb->fb.var.vmode &= ~FB_VMODE_YWRAP;
1087        }
1088
1089        return 0;
1090}
1091
1092
1093/*
1094 *    Update the `var' structure (called by fbcon.c)
1095 *
1096 *    This call looks only at yoffset and the FB_VMODE_YWRAP flag in `var'.
1097 *    Since it's called by a kernel driver, no range checking is done.
1098 */
1099static int cyber2000fb_updatevar(int con, struct fb_info *info)
1100{
1101        struct cfb_info *cfb = (struct cfb_info *)info;
1102
1103        return cyber2000fb_update_start(cfb, &fb_display[con].var);
1104}
1105
1106static int cyber2000fb_switch(int con, struct fb_info *info)
1107{
1108        struct cfb_info *cfb = (struct cfb_info *)info;
1109        struct display *disp;
1110        struct fb_cmap *cmap;
1111
1112        if (cfb->currcon >= 0) {
1113                disp = fb_display + cfb->currcon;
1114
1115                /*
1116                 * Save the old colormap and video mode.
1117                 */
1118                disp->var = cfb->fb.var;
1119                if (disp->cmap.len)
1120                        fb_copy_cmap(&cfb->fb.cmap, &disp->cmap, 0);
1121        }
1122
1123        cfb->currcon = con;
1124        disp = fb_display + con;
1125
1126        /*
1127         * Install the new colormap and change the video mode.  By default,
1128         * fbcon sets all the colormaps and video modes to the default
1129         * values at bootup.
1130         *
1131         * Really, we want to set the colourmap size depending on the
1132         * depth of the new video mode.  For now, we leave it at its
1133         * default 256 entry.
1134         */
1135        if (disp->cmap.len)
1136                cmap = &disp->cmap;
1137        else
1138                cmap = fb_default_cmap(1 << disp->var.bits_per_pixel);
1139
1140        fb_copy_cmap(cmap, &cfb->fb.cmap, 0);
1141
1142        cfb->fb.var = disp->var;
1143        cfb->fb.var.activate = FB_ACTIVATE_NOW;
1144
1145        cyber2000fb_set_var(&cfb->fb.var, con, &cfb->fb);
1146
1147        return 0;
1148}
1149
1150/*
1151 *    (Un)Blank the display.
1152 */
1153static void cyber2000fb_blank(int blank, struct fb_info *info)
1154{
1155        struct cfb_info *cfb = (struct cfb_info *)info;
1156        int i;
1157
1158        /*
1159         *  Blank the screen if blank_mode != 0, else unblank. If
1160         *  blank == NULL then the caller blanks by setting the CLUT
1161         *  (Color Look Up Table) to all black. Return 0 if blanking
1162         *  succeeded, != 0 if un-/blanking failed due to e.g. a
1163         *  video mode which doesn't support it. Implements VESA
1164         *  suspend and powerdown modes on hardware that supports
1165         *  disabling hsync/vsync:
1166         *    blank_mode == 2: suspend vsync
1167         *    blank_mode == 3: suspend hsync
1168         *    blank_mode == 4: powerdown
1169         *
1170         *  wms...Enable VESA DMPS compatible powerdown mode
1171         *  run "setterm -powersave powerdown" to take advantage
1172         */
1173     
1174        switch (blank) {
1175        case 4: /* powerdown - both sync lines down */
1176                cyber2000_grphw(0x16, 0x05, cfb);
1177                break;  
1178        case 3: /* hsync off */
1179                cyber2000_grphw(0x16, 0x01, cfb);
1180                break;  
1181        case 2: /* vsync off */
1182                cyber2000_grphw(0x16, 0x04, cfb);
1183                break;  
1184        case 1: /* soft blank */
1185                cyber2000_grphw(0x16, 0x00, cfb);
1186                for (i = 0; i < NR_PALETTE; i++) {
1187                        cyber2000fb_writeb(i, 0x3c8, cfb);
1188                        cyber2000fb_writeb(0, 0x3c9, cfb);
1189                        cyber2000fb_writeb(0, 0x3c9, cfb);
1190                        cyber2000fb_writeb(0, 0x3c9, cfb);
1191                }
1192                break;
1193        default: /* unblank */
1194                cyber2000_grphw(0x16, 0x00, cfb);
1195                for (i = 0; i < NR_PALETTE; i++) {
1196                        cyber2000fb_writeb(i, 0x3c8, cfb);
1197                        cyber2000fb_writeb(cfb->palette[i].red, 0x3c9, cfb);
1198                        cyber2000fb_writeb(cfb->palette[i].green, 0x3c9, cfb);
1199                        cyber2000fb_writeb(cfb->palette[i].blue, 0x3c9, cfb);
1200                }
1201                break;
1202        }
1203}
1204
1205/*
1206 * Get the currently displayed virtual consoles colormap.
1207 */
1208static int
1209gen_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
1210{
1211        fb_copy_cmap(&info->cmap, cmap, kspc ? 0 : 2);
1212        return 0;
1213}
1214
1215/*
1216 * Get the currently displayed virtual consoles fixed part of the display.
1217 */
1218static int
1219gen_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
1220{
1221        *fix = info->fix;
1222        return 0;
1223}
1224
1225/*
1226 * Get the current user defined part of the display.
1227 */
1228static int
1229gen_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
1230{
1231        *var = info->var;
1232        return 0;
1233}
1234
1235static struct fb_ops cyber2000fb_ops = {
1236        owner:          THIS_MODULE,
1237        fb_set_var:     cyber2000fb_set_var,
1238        fb_set_cmap:    cyber2000fb_set_cmap,
1239        fb_pan_display: cyber2000fb_pan_display,
1240        fb_get_fix:     gen_get_fix,
1241        fb_get_var:     gen_get_var,
1242        fb_get_cmap:    gen_get_cmap,
1243};
1244
1245/*
1246 * Enable access to the extended registers
1247 */
1248static void cyber2000fb_enable_extregs(struct cfb_info *cfb)
1249{
1250        cfb->func_use_count += 1;
1251
1252        if (cfb->func_use_count == 1) {
1253                int old;
1254
1255                old = cyber2000_grphr(FUNC_CTL, cfb);
1256                cyber2000_grphw(FUNC_CTL, old | FUNC_CTL_EXTREGENBL, cfb);
1257        }
1258}
1259
1260/*
1261 * Disable access to the extended registers
1262 */
1263static void cyber2000fb_disable_extregs(struct cfb_info *cfb)
1264{
1265        if (cfb->func_use_count == 1) {
1266                int old;
1267
1268                old = cyber2000_grphr(FUNC_CTL, cfb);
1269                cyber2000_grphw(FUNC_CTL, old & ~FUNC_CTL_EXTREGENBL, cfb);
1270        }
1271
1272        cfb->func_use_count -= 1;
1273}
1274
1275/*
1276 * This is the only "static" reference to the internal data structures
1277 * of this driver.  It is here solely at the moment to support the other
1278 * CyberPro modules external to this driver.
1279 */
1280static struct cfb_info          *int_cfb_info;
1281
1282/*
1283 * Attach a capture/tv driver to the core CyberX0X0 driver.
1284 */
1285int cyber2000fb_attach(struct cyberpro_info *info, int idx)
1286{
1287        if (int_cfb_info != NULL) {
1288                info->dev             = int_cfb_info->dev;
1289                info->regs            = int_cfb_info->regs;
1290                info->fb              = int_cfb_info->fb.screen_base;
1291                info->fb_size         = int_cfb_info->fb.fix.smem_len;
1292                info->enable_extregs  = cyber2000fb_enable_extregs;
1293                info->disable_extregs = cyber2000fb_disable_extregs;
1294                info->info            = int_cfb_info;
1295
1296                strncpy(info->dev_name, int_cfb_info->fb.fix.id, sizeof(info->dev_name));
1297
1298                MOD_INC_USE_COUNT;
1299        }
1300
1301        return int_cfb_info != NULL;
1302}
1303
1304/*
1305 * Detach a capture/tv driver from the core CyberX0X0 driver.
1306 */
1307void cyber2000fb_detach(int idx)
1308{
1309        MOD_DEC_USE_COUNT;
1310}
1311
1312EXPORT_SYMBOL(cyber2000fb_attach);
1313EXPORT_SYMBOL(cyber2000fb_detach);
1314
1315/*
1316 * These parameters give
1317 * 640x480, hsync 31.5kHz, vsync 60Hz
1318 */
1319static struct fb_videomode __devinitdata cyber2000fb_default_mode = {
1320        refresh:        60,
1321        xres:           640,
1322        yres:           480,
1323        pixclock:       39722,
1324        left_margin:    56,
1325        right_margin:   16,
1326        upper_margin:   34,
1327        lower_margin:   9,
1328        hsync_len:      88,
1329        vsync_len:      2,
1330        sync:           FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
1331        vmode:          FB_VMODE_NONINTERLACED
1332};
1333
1334static char igs_regs[] __devinitdata = {
1335                                        0x12, 0x00,     0x13, 0x00,
1336                                        0x16, 0x00,
1337                        0x31, 0x00,     0x32, 0x00,
1338        0x50, 0x00,     0x51, 0x00,     0x52, 0x00,     0x53, 0x00,
1339        0x54, 0x00,     0x55, 0x00,     0x56, 0x00,     0x57, 0x01,
1340        0x58, 0x00,     0x59, 0x00,     0x5a, 0x00,
1341        0x70, 0x0b,                                     0x73, 0x30,
1342        0x74, 0x0b,     0x75, 0x17,     0x76, 0x00,     0x7a, 0xc8
1343};
1344
1345/*
1346 * We need to wake up the CyberPro, and make sure its in linear memory
1347 * mode.  Unfortunately, this is specific to the platform and card that
1348 * we are running on.
1349 *
1350 * On x86 and ARM, should we be initialising the CyberPro first via the
1351 * IO registers, and then the MMIO registers to catch all cases?  Can we
1352 * end up in the situation where the chip is in MMIO mode, but not awake
1353 * on an x86 system?
1354 *
1355 * Note that on the NetWinder, the firmware automatically detects the
1356 * type, width and size, and leaves this in extended registers 0x71 and
1357 * 0x72 for us.
1358 */
1359static inline void cyberpro_init_hw(struct cfb_info *cfb, int at_boot)
1360{
1361        int i;
1362
1363        /*
1364         * Wake up the CyberPro.
1365         */
1366#ifdef __sparc__
1367#ifdef __sparc_v9__
1368#error "You loose, consult DaveM."
1369#else
1370        /*
1371         * SPARC does not have an "outb" instruction, so we generate
1372         * I/O cycles storing into a reserved memory space at
1373         * physical address 0x3000000
1374         */
1375        {
1376                unsigned char *iop;
1377
1378                iop = ioremap(0x3000000, 0x5000);
1379                if (iop == NULL) {
1380                        prom_printf("iga5000: cannot map I/O\n");
1381                        return -ENOMEM;
1382                }
1383
1384                writeb(0x18, iop + 0x46e8);
1385                writeb(0x01, iop + 0x102);
1386                writeb(0x08, iop + 0x46e8);
1387                writeb(0x33, iop + 0x3ce);
1388                writeb(0x01, iop + 0x3cf);
1389
1390                iounmap((void *)iop);
1391        }
1392#endif
1393
1394        if (at_boot) {
1395                /*
1396                 * Use mclk from BIOS.  Only read this if we're
1397                 * initialising this card for the first time.
1398                 * FIXME: what about hotplug?
1399                 */
1400                cfb->mclk_mult = cyber2000_grphr(MCLK_MULT, cfb);
1401                cfb->mclk_div  = cyber2000_grphr(MCLK_DIV, cfb);
1402        }
1403#endif
1404#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
1405        /*
1406         * x86 and MIPS are simple, we just do regular
1407         * outb's instead of cyber2000fb_writeb.
1408         */
1409        outb(0x18, 0x46e8);
1410        outb(0x01, 0x102);
1411        outb(0x08, 0x46e8);
1412        outb(0x33, 0x3ce);
1413        outb(0x01, 0x3cf);
1414
1415        if (at_boot) {
1416                /*
1417                 * Use mclk from BIOS.  Only read this if we're
1418                 * initialising this card for the first time.
1419                 * FIXME: what about hotplug?
1420                 */
1421                cfb->mclk_mult = cyber2000_grphr(MCLK_MULT, cfb);
1422                cfb->mclk_div  = cyber2000_grphr(MCLK_DIV, cfb);
1423        }
1424#endif
1425#ifdef __arm__
1426        cyber2000fb_writeb(0x18, 0x46e8, cfb);
1427        cyber2000fb_writeb(0x01, 0x102, cfb);
1428        cyber2000fb_writeb(0x08, 0x46e8, cfb);
1429        cyber2000fb_writeb(0x33, 0x3ce, cfb);
1430        cyber2000fb_writeb(0x01, 0x3cf, cfb);
1431
1432        /*
1433         * MCLK on the NetWinder and the Shark is fixed at 75MHz
1434         */
1435        cfb->mclk_mult = 0xdb;
1436        cfb->mclk_div  = 0x54;
1437#endif
1438
1439        /*
1440         * Initialise the CyberPro
1441         */
1442        for (i = 0; i < sizeof(igs_regs); i += 2)
1443                cyber2000_grphw(igs_regs[i], igs_regs[i+1], cfb);
1444
1445        if (at_boot) {
1446                /*
1447                 * get the video RAM size and width from the VGA register.
1448                 * This should have been already initialised by the BIOS,
1449                 * but if it's garbage, claim default 1MB VRAM (woody)
1450                 */
1451                cfb->mem_ctl1 = cyber2000_grphr(MEM_CTL1, cfb);
1452                cfb->mem_ctl2 = cyber2000_grphr(MEM_CTL2, cfb);
1453        } else {
1454                /*
1455                 * Reprogram the MEM_CTL1 and MEM_CTL2 registers
1456                 */
1457                cyber2000_grphw(MEM_CTL1, cfb->mem_ctl1, cfb);
1458                cyber2000_grphw(MEM_CTL2, cfb->mem_ctl2, cfb);
1459        }
1460
1461        /*
1462         * Ensure thatwe are using the correct PLL.
1463         * (CyberPro 5000's may be programmed to use
1464         * an additional set of PLLs.
1465         */
1466        cyber2000fb_writeb(0xba, 0x3ce, cfb);
1467        cyber2000fb_writeb(cyber2000fb_readb(0x3cf, cfb) & 0x80, 0x3cf, cfb);
1468}
1469
1470static struct cfb_info * __devinit
1471cyberpro_alloc_fb_info(struct pci_dev *dev, const struct pci_device_id *id, char *name)
1472{
1473        struct cfb_info *cfb;
1474
1475        cfb = kmalloc(sizeof(struct cfb_info) + sizeof(struct display) +
1476                       sizeof(u32) * 16, GFP_KERNEL);
1477
1478        if (!cfb)
1479                return NULL;
1480
1481        memset(cfb, 0, sizeof(struct cfb_info) + sizeof(struct display));
1482
1483        cfb->currcon            = -1;
1484        cfb->dev                = dev;
1485
1486        if (id->driver_data == FB_ACCEL_IGS_CYBER5000)
1487                cfb->ref_ps     = 40690; // 24.576 MHz
1488        else
1489                cfb->ref_ps     = 69842; // 14.31818 MHz (69841?)
1490
1491        cfb->divisors[0]        = 1;
1492        cfb->divisors[1]        = 2;
1493        cfb->divisors[2]        = 4;
1494
1495        if (id->driver_data == FB_ACCEL_IGS_CYBER2000)
1496                cfb->divisors[3] = 8;
1497        else
1498                cfb->divisors[3] = 6;
1499
1500        strcpy(cfb->fb.fix.id, name);
1501
1502        cfb->fb.fix.type        = FB_TYPE_PACKED_PIXELS;
1503        cfb->fb.fix.type_aux    = 0;
1504        cfb->fb.fix.xpanstep    = 0;
1505        cfb->fb.fix.ypanstep    = 1;
1506        cfb->fb.fix.ywrapstep   = 0;
1507        cfb->fb.fix.accel       = id->driver_data;
1508
1509        cfb->fb.var.nonstd      = 0;
1510        cfb->fb.var.activate    = FB_ACTIVATE_NOW;
1511        cfb->fb.var.height      = -1;
1512        cfb->fb.var.width       = -1;
1513        cfb->fb.var.accel_flags = FB_ACCELF_TEXT;
1514
1515        strcpy(cfb->fb.modename, cfb->fb.fix.id);
1516        strcpy(cfb->fb.fontname, default_font);
1517
1518        cfb->fb.fbops           = &cyber2000fb_ops;
1519        cfb->fb.changevar       = NULL;
1520        cfb->fb.switch_con      = cyber2000fb_switch;
1521        cfb->fb.updatevar       = cyber2000fb_updatevar;
1522        cfb->fb.blank           = cyber2000fb_blank;
1523        cfb->fb.flags           = FBINFO_FLAG_DEFAULT;
1524        cfb->fb.disp            = (struct display *)(cfb + 1);
1525        cfb->fb.pseudo_palette  = (void *)(cfb->fb.disp + 1);
1526
1527        fb_alloc_cmap(&cfb->fb.cmap, NR_PALETTE, 0);
1528
1529        return cfb;
1530}
1531
1532static void __devinit
1533cyberpro_free_fb_info(struct cfb_info *cfb)
1534{
1535        if (cfb) {
1536                /*
1537                 * Free the colourmap
1538                 */
1539                fb_alloc_cmap(&cfb->fb.cmap, 0, 0);
1540
1541                kfree(cfb);
1542        }
1543}
1544
1545/*
1546 * Parse Cyber2000fb options.  Usage:
1547 *  video=cyber2000:font:fontname
1548 */
1549int
1550cyber2000fb_setup(char *options)
1551{
1552        char *opt;
1553
1554        if (!options || !*options)
1555                return 0;
1556
1557        while ((opt = strsep(&options, ",")) != NULL) {
1558                if (!*opt)
1559                        continue;
1560
1561                if (strncmp(opt, "font:", 5) == 0) {
1562                        strncpy(default_font_storage, opt + 5, sizeof(default_font_storage));
1563                        default_font = default_font_storage;
1564                        continue;
1565                }
1566
1567                printk(KERN_ERR "CyberPro20x0: unknown parameter: %s\n", opt);
1568        }
1569        return 0;
1570}
1571
1572static int __devinit
1573cyberpro_probe(struct pci_dev *dev, const struct pci_device_id *id)
1574{
1575        struct cfb_info *cfb;
1576        u_int h_sync, v_sync;
1577        u_long smem_size;
1578        char name[16];
1579        int err;
1580
1581        sprintf(name, "CyberPro%4X", id->device);
1582
1583        err = pci_enable_device(dev);
1584        if (err)
1585                return err;
1586
1587        err = pci_request_regions(dev, name);
1588        if (err)
1589                return err;
1590
1591        err = -ENOMEM;
1592        cfb = cyberpro_alloc_fb_info(dev, id, name);
1593        if (!cfb)
1594                goto failed_release;
1595
1596        cfb->region = ioremap(pci_resource_start(dev, 0),
1597                              pci_resource_len(dev, 0));
1598        if (!cfb->region)
1599                goto failed_ioremap;
1600
1601        cfb->regs = cfb->region + MMIO_OFFSET;
1602
1603        cyberpro_init_hw(cfb, 1);
1604
1605        switch (cfb->mem_ctl2 & MEM_CTL2_SIZE_MASK) {
1606        case MEM_CTL2_SIZE_4MB: smem_size = 0x00400000; break;
1607        case MEM_CTL2_SIZE_2MB: smem_size = 0x00200000; break;
1608        default:                smem_size = 0x00100000; break;
1609        }
1610
1611        /*
1612         * Hmm, we _need_ a portable way of finding the address for
1613         * the remap stuff, both for mmio and for smem.
1614         */
1615        cfb->fb.fix.mmio_start = pci_resource_start(dev, 0) + MMIO_OFFSET;
1616        cfb->fb.fix.smem_start = pci_resource_start(dev, 0);
1617        cfb->fb.fix.mmio_len   = MMIO_SIZE;
1618        cfb->fb.fix.smem_len   = smem_size;
1619        cfb->fb.screen_base    = cfb->region;
1620
1621        if (!fb_find_mode(&cfb->fb.var, &cfb->fb, NULL, NULL, 0,
1622                          &cyber2000fb_default_mode, 8)) {
1623                printk("%s: no valid mode found\n", cfb->fb.fix.id);
1624                goto failed;
1625        }
1626
1627        cfb->fb.var.yres_virtual = cfb->fb.fix.smem_len * 8 /
1628                        (cfb->fb.var.bits_per_pixel * cfb->fb.var.xres_virtual);
1629
1630        if (cfb->fb.var.yres_virtual < cfb->fb.var.yres)
1631                cfb->fb.var.yres_virtual = cfb->fb.var.yres;
1632
1633        cyber2000fb_set_var(&cfb->fb.var, -1, &cfb->fb);
1634
1635        /*
1636         * Calculate the hsync and vsync frequencies.  Note that
1637         * we split the 1e12 constant up so that we can preserve
1638         * the precision and fit the results into 32-bit registers.
1639         *  (1953125000 * 512 = 1e12)
1640         */
1641        h_sync = 1953125000 / cfb->fb.var.pixclock;
1642        h_sync = h_sync * 512 / (cfb->fb.var.xres + cfb->fb.var.left_margin +
1643                 cfb->fb.var.right_margin + cfb->fb.var.hsync_len);
1644        v_sync = h_sync / (cfb->fb.var.yres + cfb->fb.var.upper_margin +
1645                 cfb->fb.var.lower_margin + cfb->fb.var.vsync_len);
1646
1647        printk(KERN_INFO "%s: %dkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
1648                cfb->fb.fix.id, cfb->fb.fix.smem_len >> 10,
1649                cfb->fb.var.xres, cfb->fb.var.yres,
1650                h_sync / 1000, h_sync % 1000, v_sync);
1651
1652        err = register_framebuffer(&cfb->fb);
1653        if (err < 0)
1654                goto failed;
1655
1656        /*
1657         * Our driver data
1658         */
1659        pci_set_drvdata(dev, cfb);
1660        if (int_cfb_info == NULL)
1661                int_cfb_info = cfb;
1662
1663        return 0;
1664
1665failed:
1666        iounmap(cfb->region);
1667failed_ioremap:
1668        cyberpro_free_fb_info(cfb);
1669failed_release:
1670        pci_release_regions(dev);
1671
1672        return err;
1673}
1674
1675static void __devexit cyberpro_remove(struct pci_dev *dev)
1676{
1677        struct cfb_info *cfb = pci_get_drvdata(dev);
1678
1679        if (cfb) {
1680                /*
1681                 * If unregister_framebuffer fails, then
1682                 * we will be leaving hooks that could cause
1683                 * oopsen laying around.
1684                 */
1685                if (unregister_framebuffer(&cfb->fb))
1686                        printk(KERN_WARNING "%s: danger Will Robinson, "
1687                                "danger danger!  Oopsen imminent!\n",
1688                                cfb->fb.fix.id);
1689                iounmap(cfb->region);
1690                cyberpro_free_fb_info(cfb);
1691
1692                /*
1693                 * Ensure that the driver data is no longer
1694                 * valid.
1695                 */
1696                pci_set_drvdata(dev, NULL);
1697                if (cfb == int_cfb_info)
1698                        int_cfb_info = NULL;
1699
1700                pci_release_regions(dev);
1701        }
1702}
1703
1704static int cyberpro_suspend(struct pci_dev *dev, u32 state)
1705{
1706        return 0;
1707}
1708
1709/*
1710 * Re-initialise the CyberPro hardware
1711 */
1712static int cyberpro_resume(struct pci_dev *dev)
1713{
1714        struct cfb_info *cfb = pci_get_drvdata(dev);
1715
1716        if (cfb) {
1717                cyberpro_init_hw(cfb, 0);
1718
1719                /*
1720                 * Restore the old video mode and the palette.
1721                 * We also need to tell fbcon to redraw the console.
1722                 */
1723                cfb->fb.var.activate = FB_ACTIVATE_NOW;
1724                cyber2000fb_set_var(&cfb->fb.var, -1, &cfb->fb);
1725        }
1726
1727        return 0;
1728}
1729
1730static struct pci_device_id cyberpro_pci_table[] __devinitdata = {
1731        { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_2000,
1732                PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_IGS_CYBER2000 },
1733        { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_2010,
1734                PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_IGS_CYBER2010 },
1735        { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_5000,
1736                PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_IGS_CYBER5000 },
1737        { 0, }
1738};
1739
1740static struct pci_driver cyberpro_driver = {
1741        name:           "CyberPro",
1742        probe:          cyberpro_probe,
1743        remove:         __devexit_p(cyberpro_remove),
1744        suspend:        cyberpro_suspend,
1745        resume:         cyberpro_resume,
1746        id_table:       cyberpro_pci_table
1747};
1748
1749/*
1750 * I don't think we can use the "module_init" stuff here because
1751 * the fbcon stuff may not be initialised yet.  Hence the #ifdef
1752 * around module_init.
1753 */
1754int __init cyber2000fb_init(void)
1755{
1756        return pci_module_init(&cyberpro_driver);
1757}
1758
1759static void __exit cyberpro_exit(void)
1760{
1761        pci_unregister_driver(&cyberpro_driver);
1762}
1763
1764#ifdef MODULE
1765module_init(cyber2000fb_init);
1766#endif
1767module_exit(cyberpro_exit);
1768
1769MODULE_AUTHOR("Russell King");
1770MODULE_DESCRIPTION("CyberPro 2000, 2010 and 5000 framebuffer driver");
1771MODULE_DEVICE_TABLE(pci,cyberpro_pci_table);
1772MODULE_LICENSE("GPL");
1773
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.