linux/drivers/video/fbdev/matrox/matroxfb_crtc2.c
<<
>>
Prefs
   1/*
   2 *
   3 * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
   4 *
   5 * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
   6 *
   7 * Portions Copyright (c) 2001 Matrox Graphics Inc.
   8 *
   9 * Version: 1.65 2002/08/14
  10 *
  11 */
  12
  13#include "matroxfb_maven.h"
  14#include "matroxfb_crtc2.h"
  15#include "matroxfb_misc.h"
  16#include "matroxfb_DAC1064.h"
  17#include <linux/matroxfb.h>
  18#include <linux/slab.h>
  19#include <linux/uaccess.h>
  20
  21/* **************************************************** */
  22
  23static int mem = 8192;
  24
  25module_param(mem, int, 0);
  26MODULE_PARM_DESC(mem, "Memory size reserved for dualhead (default=8MB)");
  27
  28/* **************************************************** */
  29
  30static int matroxfb_dh_setcolreg(unsigned regno, unsigned red, unsigned green,
  31                unsigned blue, unsigned transp, struct fb_info* info) {
  32        u_int32_t col;
  33#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
  34
  35        if (regno >= 16)
  36                return 1;
  37        if (m2info->fbcon.var.grayscale) {
  38                /* gray = 0.30*R + 0.59*G + 0.11*B */
  39                red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
  40        }
  41        red = CNVT_TOHW(red, m2info->fbcon.var.red.length);
  42        green = CNVT_TOHW(green, m2info->fbcon.var.green.length);
  43        blue = CNVT_TOHW(blue, m2info->fbcon.var.blue.length);
  44        transp = CNVT_TOHW(transp, m2info->fbcon.var.transp.length);
  45
  46        col = (red << m2info->fbcon.var.red.offset)     |
  47              (green << m2info->fbcon.var.green.offset) |
  48              (blue << m2info->fbcon.var.blue.offset)   |
  49              (transp << m2info->fbcon.var.transp.offset);
  50
  51        switch (m2info->fbcon.var.bits_per_pixel) {
  52                case 16:
  53                        m2info->cmap[regno] = col | (col << 16);
  54                        break;
  55                case 32:
  56                        m2info->cmap[regno] = col;
  57                        break;
  58        }
  59        return 0;
  60#undef m2info
  61}
  62
  63static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info,
  64                struct my_timming* mt,
  65                int mode,
  66                unsigned int pos) {
  67        u_int32_t tmp;
  68        u_int32_t datactl;
  69        struct matrox_fb_info *minfo = m2info->primary_dev;
  70
  71        switch (mode) {
  72                case 15:
  73                        tmp = 0x00200000;
  74                        break;
  75                case 16:
  76                        tmp = 0x00400000;
  77                        break;
  78/*              case 32: */
  79                default:
  80                        tmp = 0x00800000;
  81                        break;
  82        }
  83        tmp |= 0x00000001;      /* enable CRTC2 */
  84        datactl = 0;
  85        if (minfo->outputs[1].src == MATROXFB_SRC_CRTC2) {
  86                if (minfo->devflags.g450dac) {
  87                        tmp |= 0x00000006; /* source from secondary pixel PLL */
  88                        /* no vidrst when in monitor mode */
  89                        if (minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) {
  90                                tmp |=  0xC0001000; /* Enable H/V vidrst */
  91                        }
  92                } else {
  93                        tmp |= 0x00000002; /* source from VDOCLK */
  94                        tmp |= 0xC0000000; /* enable vvidrst & hvidrst */
  95                        /* MGA TVO is our clock source */
  96                }
  97        } else if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) {
  98                tmp |= 0x00000004; /* source from pixclock */
  99                /* PIXPLL is our clock source */
 100        }
 101        if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) {
 102                tmp |= 0x00100000;      /* connect CRTC2 to DAC */
 103        }
 104        if (mt->interlaced) {
 105                tmp |= 0x02000000;      /* interlaced, second field is bigger, as G450 apparently ignores it */
 106                mt->VDisplay >>= 1;
 107                mt->VSyncStart >>= 1;
 108                mt->VSyncEnd >>= 1;
 109                mt->VTotal >>= 1;
 110        }
 111        if ((mt->HTotal & 7) == 2) {
 112                datactl |= 0x00000010;
 113                mt->HTotal &= ~7;
 114        }
 115        tmp |= 0x10000000;      /* 0x10000000 is VIDRST polarity */
 116        mga_outl(0x3C14, ((mt->HDisplay - 8) << 16) | (mt->HTotal - 8));
 117        mga_outl(0x3C18, ((mt->HSyncEnd - 8) << 16) | (mt->HSyncStart - 8));
 118        mga_outl(0x3C1C, ((mt->VDisplay - 1) << 16) | (mt->VTotal - 1));
 119        mga_outl(0x3C20, ((mt->VSyncEnd - 1) << 16) | (mt->VSyncStart - 1));
 120        mga_outl(0x3C24, ((mt->VSyncStart) << 16) | (mt->HSyncStart));  /* preload */
 121        {
 122                u_int32_t linelen = m2info->fbcon.var.xres_virtual * (m2info->fbcon.var.bits_per_pixel >> 3);
 123                if (tmp & 0x02000000) {
 124                        /* field #0 is smaller, so... */
 125                        mga_outl(0x3C2C, pos);                  /* field #1 vmemory start */
 126                        mga_outl(0x3C28, pos + linelen);        /* field #0 vmemory start */
 127                        linelen <<= 1;
 128                        m2info->interlaced = 1;
 129                } else {
 130                        mga_outl(0x3C28, pos);          /* vmemory start */
 131                        m2info->interlaced = 0;
 132                }
 133                mga_outl(0x3C40, linelen);
 134        }
 135        mga_outl(0x3C4C, datactl);      /* data control */
 136        if (tmp & 0x02000000) {
 137                int i;
 138
 139                mga_outl(0x3C10, tmp & ~0x02000000);
 140                for (i = 0; i < 2; i++) {
 141                        unsigned int nl;
 142                        unsigned int lastl = 0;
 143
 144                        while ((nl = mga_inl(0x3C48) & 0xFFF) >= lastl) {
 145                                lastl = nl;
 146                        }
 147                }
 148        }
 149        mga_outl(0x3C10, tmp);
 150        minfo->hw.crtc2.ctl = tmp;
 151
 152        tmp = mt->VDisplay << 16;       /* line compare */
 153        if (mt->sync & FB_SYNC_HOR_HIGH_ACT)
 154                tmp |= 0x00000100;
 155        if (mt->sync & FB_SYNC_VERT_HIGH_ACT)
 156                tmp |= 0x00000200;
 157        mga_outl(0x3C44, tmp);
 158}
 159
 160static void matroxfb_dh_disable(struct matroxfb_dh_fb_info* m2info) {
 161        struct matrox_fb_info *minfo = m2info->primary_dev;
 162
 163        mga_outl(0x3C10, 0x00000004);   /* disable CRTC2, CRTC1->DAC1, PLL as clock source */
 164        minfo->hw.crtc2.ctl = 0x00000004;
 165}
 166
 167static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info,
 168                struct fb_var_screeninfo* var) {
 169        unsigned int pos;
 170        unsigned int linelen;
 171        unsigned int pixelsize;
 172        struct matrox_fb_info *minfo = m2info->primary_dev;
 173
 174        m2info->fbcon.var.xoffset = var->xoffset;
 175        m2info->fbcon.var.yoffset = var->yoffset;
 176        pixelsize = m2info->fbcon.var.bits_per_pixel >> 3;
 177        linelen = m2info->fbcon.var.xres_virtual * pixelsize;
 178        pos = m2info->fbcon.var.yoffset * linelen + m2info->fbcon.var.xoffset * pixelsize;
 179        pos += m2info->video.offbase;
 180        if (m2info->interlaced) {
 181                mga_outl(0x3C2C, pos);
 182                mga_outl(0x3C28, pos + linelen);
 183        } else {
 184                mga_outl(0x3C28, pos);
 185        }
 186}
 187
 188static int matroxfb_dh_decode_var(struct matroxfb_dh_fb_info* m2info,
 189                struct fb_var_screeninfo* var,
 190                int *visual,
 191                int *video_cmap_len,
 192                int *mode) {
 193        unsigned int mask;
 194        unsigned int memlen;
 195        unsigned int vramlen;
 196
 197        switch (var->bits_per_pixel) {
 198                case 16:        mask = 0x1F;
 199                                break;
 200                case 32:        mask = 0x0F;
 201                                break;
 202                default:        return -EINVAL;
 203        }
 204        vramlen = m2info->video.len_usable;
 205        if (var->yres_virtual < var->yres)
 206                var->yres_virtual = var->yres;
 207        if (var->xres_virtual < var->xres)
 208                var->xres_virtual = var->xres;
 209        var->xres_virtual = (var->xres_virtual + mask) & ~mask;
 210        if (var->yres_virtual > 32767)
 211                return -EINVAL;
 212        memlen = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel >> 3);
 213        if (memlen > vramlen)
 214                return -EINVAL;
 215        if (var->xoffset + var->xres > var->xres_virtual)
 216                var->xoffset = var->xres_virtual - var->xres;
 217        if (var->yoffset + var->yres > var->yres_virtual)
 218                var->yoffset = var->yres_virtual - var->yres;
 219
 220        var->xres &= ~7;
 221        var->left_margin &= ~7;
 222        var->right_margin &= ~7;
 223        var->hsync_len &= ~7;
 224
 225        *mode = var->bits_per_pixel;
 226        if (var->bits_per_pixel == 16) {
 227                if (var->green.length == 5) {
 228                        var->red.offset = 10;
 229                        var->red.length = 5;
 230                        var->green.offset = 5;
 231                        var->green.length = 5;
 232                        var->blue.offset = 0;
 233                        var->blue.length = 5;
 234                        var->transp.offset = 15;
 235                        var->transp.length = 1;
 236                        *mode = 15;
 237                } else {
 238                        var->red.offset = 11;
 239                        var->red.length = 5;
 240                        var->green.offset = 5;
 241                        var->green.length = 6;
 242                        var->blue.offset = 0;
 243                        var->blue.length = 5;
 244                        var->transp.offset = 0;
 245                        var->transp.length = 0;
 246                }
 247        } else {
 248                        var->red.offset = 16;
 249                        var->red.length = 8;
 250                        var->green.offset = 8;
 251                        var->green.length = 8;
 252                        var->blue.offset = 0;
 253                        var->blue.length = 8;
 254                        var->transp.offset = 24;
 255                        var->transp.length = 8;
 256        }
 257        *visual = FB_VISUAL_TRUECOLOR;
 258        *video_cmap_len = 16;
 259        return 0;
 260}
 261
 262static int matroxfb_dh_open(struct fb_info* info, int user) {
 263#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
 264        struct matrox_fb_info *minfo = m2info->primary_dev;
 265
 266        if (minfo) {
 267                int err;
 268
 269                if (minfo->dead) {
 270                        return -ENXIO;
 271                }
 272                err = minfo->fbops.fb_open(&minfo->fbcon, user);
 273                if (err) {
 274                        return err;
 275                }
 276        }
 277        return 0;
 278#undef m2info
 279}
 280
 281static int matroxfb_dh_release(struct fb_info* info, int user) {
 282#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
 283        int err = 0;
 284        struct matrox_fb_info *minfo = m2info->primary_dev;
 285
 286        if (minfo) {
 287                err = minfo->fbops.fb_release(&minfo->fbcon, user);
 288        }
 289        return err;
 290#undef m2info
 291}
 292
 293/*
 294 * This function is called before the register_framebuffer so
 295 * no locking is needed.
 296 */
 297static void matroxfb_dh_init_fix(struct matroxfb_dh_fb_info *m2info)
 298{
 299        struct fb_fix_screeninfo *fix = &m2info->fbcon.fix;
 300
 301        strcpy(fix->id, "MATROX DH");
 302
 303        fix->smem_start = m2info->video.base;
 304        fix->smem_len = m2info->video.len_usable;
 305        fix->ypanstep = 1;
 306        fix->ywrapstep = 0;
 307        fix->xpanstep = 8;      /* TBD */
 308        fix->mmio_start = m2info->mmio.base;
 309        fix->mmio_len = m2info->mmio.len;
 310        fix->accel = 0;         /* no accel... */
 311}
 312
 313static int matroxfb_dh_check_var(struct fb_var_screeninfo* var, struct fb_info* info) {
 314#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
 315        int visual;
 316        int cmap_len;
 317        int mode;
 318
 319        return matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode);
 320#undef m2info
 321}
 322
 323static int matroxfb_dh_set_par(struct fb_info* info) {
 324#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
 325        int visual;
 326        int cmap_len;
 327        int mode;
 328        int err;
 329        struct fb_var_screeninfo* var = &info->var;
 330        struct matrox_fb_info *minfo = m2info->primary_dev;
 331
 332        if ((err = matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode)) != 0)
 333                return err;
 334        /* cmap */
 335        {
 336                m2info->fbcon.screen_base = vaddr_va(m2info->video.vbase);
 337                m2info->fbcon.fix.visual = visual;
 338                m2info->fbcon.fix.type = FB_TYPE_PACKED_PIXELS;
 339                m2info->fbcon.fix.type_aux = 0;
 340                m2info->fbcon.fix.line_length = (var->xres_virtual * var->bits_per_pixel) >> 3;
 341        }
 342        {
 343                struct my_timming mt;
 344                unsigned int pos;
 345                int out;
 346                int cnt;
 347
 348                matroxfb_var2my(&m2info->fbcon.var, &mt);
 349                mt.crtc = MATROXFB_SRC_CRTC2;
 350                /* CRTC2 delay */
 351                mt.delay = 34;
 352
 353                pos = (m2info->fbcon.var.yoffset * m2info->fbcon.var.xres_virtual + m2info->fbcon.var.xoffset) * m2info->fbcon.var.bits_per_pixel >> 3;
 354                pos += m2info->video.offbase;
 355                cnt = 0;
 356                down_read(&minfo->altout.lock);
 357                for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
 358                        if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) {
 359                                cnt++;
 360                                if (minfo->outputs[out].output->compute) {
 361                                        minfo->outputs[out].output->compute(minfo->outputs[out].data, &mt);
 362                                }
 363                        }
 364                }
 365                minfo->crtc2.pixclock = mt.pixclock;
 366                minfo->crtc2.mnp = mt.mnp;
 367                up_read(&minfo->altout.lock);
 368                if (cnt) {
 369                        matroxfb_dh_restore(m2info, &mt, mode, pos);
 370                } else {
 371                        matroxfb_dh_disable(m2info);
 372                }
 373                DAC1064_global_init(minfo);
 374                DAC1064_global_restore(minfo);
 375                down_read(&minfo->altout.lock);
 376                for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
 377                        if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2 &&
 378                            minfo->outputs[out].output->program) {
 379                                minfo->outputs[out].output->program(minfo->outputs[out].data);
 380                        }
 381                }
 382                for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
 383                        if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2 &&
 384                            minfo->outputs[out].output->start) {
 385                                minfo->outputs[out].output->start(minfo->outputs[out].data);
 386                        }
 387                }
 388                up_read(&minfo->altout.lock);
 389        }
 390        m2info->initialized = 1;
 391        return 0;
 392#undef m2info
 393}
 394
 395static int matroxfb_dh_pan_display(struct fb_var_screeninfo* var, struct fb_info* info) {
 396#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
 397        matroxfb_dh_pan_var(m2info, var);
 398        return 0;
 399#undef m2info
 400}
 401
 402static int matroxfb_dh_get_vblank(const struct matroxfb_dh_fb_info* m2info, struct fb_vblank* vblank) {
 403        struct matrox_fb_info *minfo = m2info->primary_dev;
 404
 405        matroxfb_enable_irq(minfo, 0);
 406        memset(vblank, 0, sizeof(*vblank));
 407        vblank->flags = FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VBLANK;
 408        /* mask out reserved bits + field number (odd/even) */
 409        vblank->vcount = mga_inl(0x3C48) & 0x000007FF;
 410        /* compatibility stuff */
 411        if (vblank->vcount >= m2info->fbcon.var.yres)
 412                vblank->flags |= FB_VBLANK_VBLANKING;
 413        if (test_bit(0, &minfo->irq_flags)) {
 414                vblank->flags |= FB_VBLANK_HAVE_COUNT;
 415                /* Only one writer, aligned int value...
 416                   it should work without lock and without atomic_t */
 417                vblank->count = minfo->crtc2.vsync.cnt;
 418        }
 419        return 0;
 420}
 421
 422static int matroxfb_dh_ioctl(struct fb_info *info,
 423                unsigned int cmd,
 424                unsigned long arg)
 425{
 426#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
 427        struct matrox_fb_info *minfo = m2info->primary_dev;
 428
 429        DBG(__func__)
 430
 431        switch (cmd) {
 432                case FBIOGET_VBLANK:
 433                        {
 434                                struct fb_vblank vblank;
 435                                int err;
 436
 437                                err = matroxfb_dh_get_vblank(m2info, &vblank);
 438                                if (err)
 439                                        return err;
 440                                if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
 441                                        return -EFAULT;
 442                                return 0;
 443                        }
 444                case FBIO_WAITFORVSYNC:
 445                        {
 446                                u_int32_t crt;
 447
 448                                if (get_user(crt, (u_int32_t __user *)arg))
 449                                        return -EFAULT;
 450
 451                                if (crt != 0)
 452                                        return -ENODEV;
 453                                return matroxfb_wait_for_sync(minfo, 1);
 454                        }
 455                case MATROXFB_SET_OUTPUT_MODE:
 456                case MATROXFB_GET_OUTPUT_MODE:
 457                case MATROXFB_GET_ALL_OUTPUTS:
 458                        {
 459                                return minfo->fbcon.fbops->fb_ioctl(&minfo->fbcon, cmd, arg);
 460                        }
 461                case MATROXFB_SET_OUTPUT_CONNECTION:
 462                        {
 463                                u_int32_t tmp;
 464                                int out;
 465                                int changes;
 466
 467                                if (get_user(tmp, (u_int32_t __user *)arg))
 468                                        return -EFAULT;
 469                                for (out = 0; out < 32; out++) {
 470                                        if (tmp & (1 << out)) {
 471                                                if (out >= MATROXFB_MAX_OUTPUTS)
 472                                                        return -ENXIO;
 473                                                if (!minfo->outputs[out].output)
 474                                                        return -ENXIO;
 475                                                switch (minfo->outputs[out].src) {
 476                                                        case MATROXFB_SRC_NONE:
 477                                                        case MATROXFB_SRC_CRTC2:
 478                                                                break;
 479                                                        default:
 480                                                                return -EBUSY;
 481                                                }
 482                                        }
 483                                }
 484                                if (minfo->devflags.panellink) {
 485                                        if (tmp & MATROXFB_OUTPUT_CONN_DFP)
 486                                                return -EINVAL;
 487                                        if ((minfo->outputs[2].src == MATROXFB_SRC_CRTC1) && tmp)
 488                                                return -EBUSY;
 489                                }
 490                                changes = 0;
 491                                for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
 492                                        if (tmp & (1 << out)) {
 493                                                if (minfo->outputs[out].src != MATROXFB_SRC_CRTC2) {
 494                                                        changes = 1;
 495                                                        minfo->outputs[out].src = MATROXFB_SRC_CRTC2;
 496                                                }
 497                                        } else if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) {
 498                                                changes = 1;
 499                                                minfo->outputs[out].src = MATROXFB_SRC_NONE;
 500                                        }
 501                                }
 502                                if (!changes)
 503                                        return 0;
 504                                matroxfb_dh_set_par(info);
 505                                return 0;
 506                        }
 507                case MATROXFB_GET_OUTPUT_CONNECTION:
 508                        {
 509                                u_int32_t conn = 0;
 510                                int out;
 511
 512                                for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
 513                                        if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) {
 514                                                conn |= 1 << out;
 515                                        }
 516                                }
 517                                if (put_user(conn, (u_int32_t __user *)arg))
 518                                        return -EFAULT;
 519                                return 0;
 520                        }
 521                case MATROXFB_GET_AVAILABLE_OUTPUTS:
 522                        {
 523                                u_int32_t tmp = 0;
 524                                int out;
 525
 526                                for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
 527                                        if (minfo->outputs[out].output) {
 528                                                switch (minfo->outputs[out].src) {
 529                                                        case MATROXFB_SRC_NONE:
 530                                                        case MATROXFB_SRC_CRTC2:
 531                                                                tmp |= 1 << out;
 532                                                                break;
 533                                                }
 534                                        }
 535                                }
 536                                if (minfo->devflags.panellink) {
 537                                        tmp &= ~MATROXFB_OUTPUT_CONN_DFP;
 538                                        if (minfo->outputs[2].src == MATROXFB_SRC_CRTC1) {
 539                                                tmp = 0;
 540                                        }
 541                                }
 542                                if (put_user(tmp, (u_int32_t __user *)arg))
 543                                        return -EFAULT;
 544                                return 0;
 545                        }
 546        }
 547        return -ENOTTY;
 548#undef m2info
 549}
 550
 551static int matroxfb_dh_blank(int blank, struct fb_info* info) {
 552#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
 553        switch (blank) {
 554                case 1:
 555                case 2:
 556                case 3:
 557                case 4:
 558                default:;
 559        }
 560        /* do something... */
 561        return 0;
 562#undef m2info
 563}
 564
 565static struct fb_ops matroxfb_dh_ops = {
 566        .owner =        THIS_MODULE,
 567        .fb_open =      matroxfb_dh_open,
 568        .fb_release =   matroxfb_dh_release,
 569        .fb_check_var = matroxfb_dh_check_var,
 570        .fb_set_par =   matroxfb_dh_set_par,
 571        .fb_setcolreg = matroxfb_dh_setcolreg,
 572        .fb_pan_display =matroxfb_dh_pan_display,
 573        .fb_blank =     matroxfb_dh_blank,
 574        .fb_ioctl =     matroxfb_dh_ioctl,
 575        .fb_fillrect =  cfb_fillrect,
 576        .fb_copyarea =  cfb_copyarea,
 577        .fb_imageblit = cfb_imageblit,
 578};
 579
 580static struct fb_var_screeninfo matroxfb_dh_defined = {
 581                640,480,640,480,/* W,H, virtual W,H */
 582                0,0,            /* offset */
 583                32,             /* depth */
 584                0,              /* gray */
 585                {0,0,0},        /* R */
 586                {0,0,0},        /* G */
 587                {0,0,0},        /* B */
 588                {0,0,0},        /* alpha */
 589                0,              /* nonstd */
 590                FB_ACTIVATE_NOW,
 591                -1,-1,          /* display size */
 592                0,              /* accel flags */
 593                39721L,48L,16L,33L,10L,
 594                96L,2,0,        /* no sync info */
 595                FB_VMODE_NONINTERLACED,
 596};
 597
 598static int matroxfb_dh_regit(const struct matrox_fb_info *minfo,
 599                             struct matroxfb_dh_fb_info *m2info)
 600{
 601#define minfo (m2info->primary_dev)
 602        void* oldcrtc2;
 603
 604        m2info->fbcon.fbops = &matroxfb_dh_ops;
 605        m2info->fbcon.flags = FBINFO_FLAG_DEFAULT;
 606        m2info->fbcon.flags |= FBINFO_HWACCEL_XPAN |
 607                               FBINFO_HWACCEL_YPAN;
 608        m2info->fbcon.pseudo_palette = m2info->cmap;
 609        fb_alloc_cmap(&m2info->fbcon.cmap, 256, 1);
 610
 611        if (mem < 64)
 612                mem *= 1024;
 613        if (mem < 64*1024)
 614                mem *= 1024;
 615        mem &= ~0x00000FFF;     /* PAGE_MASK? */
 616        if (minfo->video.len_usable + mem <= minfo->video.len)
 617                m2info->video.offbase = minfo->video.len - mem;
 618        else if (minfo->video.len < mem) {
 619                return -ENOMEM;
 620        } else { /* check yres on first head... */
 621                m2info->video.borrowed = mem;
 622                minfo->video.len_usable -= mem;
 623                m2info->video.offbase = minfo->video.len_usable;
 624        }
 625        m2info->video.base = minfo->video.base + m2info->video.offbase;
 626        m2info->video.len = m2info->video.len_usable = m2info->video.len_maximum = mem;
 627        m2info->video.vbase.vaddr = vaddr_va(minfo->video.vbase) + m2info->video.offbase;
 628        m2info->mmio.base = minfo->mmio.base;
 629        m2info->mmio.vbase = minfo->mmio.vbase;
 630        m2info->mmio.len = minfo->mmio.len;
 631
 632        matroxfb_dh_init_fix(m2info);
 633        if (register_framebuffer(&m2info->fbcon)) {
 634                return -ENXIO;
 635        }
 636        if (!m2info->initialized)
 637                fb_set_var(&m2info->fbcon, &matroxfb_dh_defined);
 638        down_write(&minfo->crtc2.lock);
 639        oldcrtc2 = minfo->crtc2.info;
 640        minfo->crtc2.info = m2info;
 641        up_write(&minfo->crtc2.lock);
 642        if (oldcrtc2) {
 643                printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 already present: %p\n",
 644                        oldcrtc2);
 645        }
 646        return 0;
 647#undef minfo
 648}
 649
 650/* ************************** */
 651
 652static int matroxfb_dh_registerfb(struct matroxfb_dh_fb_info* m2info) {
 653#define minfo (m2info->primary_dev)
 654        if (matroxfb_dh_regit(minfo, m2info)) {
 655                printk(KERN_ERR "matroxfb_crtc2: secondary head failed to register\n");
 656                return -1;
 657        }
 658        printk(KERN_INFO "matroxfb_crtc2: secondary head of fb%u was registered as fb%u\n",
 659                minfo->fbcon.node, m2info->fbcon.node);
 660        m2info->fbcon_registered = 1;
 661        return 0;
 662#undef minfo
 663}
 664
 665static void matroxfb_dh_deregisterfb(struct matroxfb_dh_fb_info* m2info) {
 666#define minfo (m2info->primary_dev)
 667        if (m2info->fbcon_registered) {
 668                int id;
 669                struct matroxfb_dh_fb_info* crtc2;
 670
 671                down_write(&minfo->crtc2.lock);
 672                crtc2 = minfo->crtc2.info;
 673                if (crtc2 == m2info)
 674                        minfo->crtc2.info = NULL;
 675                up_write(&minfo->crtc2.lock);
 676                if (crtc2 != m2info) {
 677                        printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 mismatch at unload: %p != %p\n",
 678                                crtc2, m2info);
 679                        printk(KERN_ERR "matroxfb_crtc2: Expect kernel crash after module unload.\n");
 680                        return;
 681                }
 682                id = m2info->fbcon.node;
 683                unregister_framebuffer(&m2info->fbcon);
 684                /* return memory back to primary head */
 685                minfo->video.len_usable += m2info->video.borrowed;
 686                printk(KERN_INFO "matroxfb_crtc2: fb%u unregistered\n", id);
 687                m2info->fbcon_registered = 0;
 688        }
 689#undef minfo
 690}
 691
 692static void* matroxfb_crtc2_probe(struct matrox_fb_info* minfo) {
 693        struct matroxfb_dh_fb_info* m2info;
 694
 695        /* hardware is CRTC2 incapable... */
 696        if (!minfo->devflags.crtc2)
 697                return NULL;
 698        m2info = kzalloc(sizeof(*m2info), GFP_KERNEL);
 699        if (!m2info) {
 700                printk(KERN_ERR "matroxfb_crtc2: Not enough memory for CRTC2 control structs\n");
 701                return NULL;
 702        }
 703        m2info->primary_dev = minfo;
 704        if (matroxfb_dh_registerfb(m2info)) {
 705                kfree(m2info);
 706                printk(KERN_ERR "matroxfb_crtc2: CRTC2 framebuffer failed to register\n");
 707                return NULL;
 708        }
 709        return m2info;
 710}
 711
 712static void matroxfb_crtc2_remove(struct matrox_fb_info* minfo, void* crtc2) {
 713        matroxfb_dh_deregisterfb(crtc2);
 714        kfree(crtc2);
 715}
 716
 717static struct matroxfb_driver crtc2 = {
 718                .name =         "Matrox G400 CRTC2",
 719                .probe =        matroxfb_crtc2_probe,
 720                .remove =       matroxfb_crtc2_remove };
 721
 722static int matroxfb_crtc2_init(void) {
 723        if (fb_get_options("matrox_crtc2fb", NULL))
 724                return -ENODEV;
 725
 726        matroxfb_register_driver(&crtc2);
 727        return 0;
 728}
 729
 730static void matroxfb_crtc2_exit(void) {
 731        matroxfb_unregister_driver(&crtc2);
 732}
 733
 734MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
 735MODULE_DESCRIPTION("Matrox G400 CRTC2 driver");
 736MODULE_LICENSE("GPL");
 737module_init(matroxfb_crtc2_init);
 738module_exit(matroxfb_crtc2_exit);
 739/* we do not have __setup() yet */
 740