linux/drivers/video/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/uaccess.h>
  19
  20/* **************************************************** */
  21
  22static int mem = 8192;
  23
  24module_param(mem, int, 0);
  25MODULE_PARM_DESC(mem, "Memory size reserved for dualhead (default=8MB)");
  26
  27/* **************************************************** */
  28
  29static int matroxfb_dh_setcolreg(unsigned regno, unsigned red, unsigned green,
  30                unsigned blue, unsigned transp, struct fb_info* info) {
  31        u_int32_t col;
  32#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
  33
  34        if (regno >= 16)
  35                return 1;
  36        if (m2info->fbcon.var.grayscale) {
  37                /* gray = 0.30*R + 0.59*G + 0.11*B */
  38                red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
  39        }
  40        red = CNVT_TOHW(red, m2info->fbcon.var.red.length);
  41        green = CNVT_TOHW(green, m2info->fbcon.var.green.length);
  42        blue = CNVT_TOHW(blue, m2info->fbcon.var.blue.length);
  43        transp = CNVT_TOHW(transp, m2info->fbcon.var.transp.length);
  44
  45        col = (red << m2info->fbcon.var.red.offset)     |
  46              (green << m2info->fbcon.var.green.offset) |
  47              (blue << m2info->fbcon.var.blue.offset)   |
  48              (transp << m2info->fbcon.var.transp.offset);
  49
  50        switch (m2info->fbcon.var.bits_per_pixel) {
  51                case 16:
  52                        m2info->cmap[regno] = col | (col << 16);
  53                        break;
  54                case 32:
  55                        m2info->cmap[regno] = col;
  56                        break;
  57        }
  58        return 0;
  59#undef m2info
  60}
  61
  62static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info,
  63                struct my_timming* mt,
  64                int mode,
  65                unsigned int pos) {
  66        u_int32_t tmp;
  67        u_int32_t datactl;
  68        struct matrox_fb_info *minfo = m2info->primary_dev;
  69
  70        switch (mode) {
  71                case 15:
  72                        tmp = 0x00200000;
  73                        break;
  74                case 16:
  75                        tmp = 0x00400000;
  76                        break;
  77/*              case 32: */
  78                default:
  79                        tmp = 0x00800000;
  80                        break;
  81        }
  82        tmp |= 0x00000001;      /* enable CRTC2 */
  83        datactl = 0;
  84        if (minfo->outputs[1].src == MATROXFB_SRC_CRTC2) {
  85                if (minfo->devflags.g450dac) {
  86                        tmp |= 0x00000006; /* source from secondary pixel PLL */
  87                        /* no vidrst when in monitor mode */
  88                        if (minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) {
  89                                tmp |=  0xC0001000; /* Enable H/V vidrst */
  90                        }
  91                } else {
  92                        tmp |= 0x00000002; /* source from VDOCLK */
  93                        tmp |= 0xC0000000; /* enable vvidrst & hvidrst */
  94                        /* MGA TVO is our clock source */
  95                }
  96        } else if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) {
  97                tmp |= 0x00000004; /* source from pixclock */
  98                /* PIXPLL is our clock source */
  99        }
 100        if (minfo->outputs[0].src == MATROXFB_SRC_CRTC2) {
 101                tmp |= 0x00100000;      /* connect CRTC2 to DAC */
 102        }
 103        if (mt->interlaced) {
 104                tmp |= 0x02000000;      /* interlaced, second field is bigger, as G450 apparently ignores it */
 105                mt->VDisplay >>= 1;
 106                mt->VSyncStart >>= 1;
 107                mt->VSyncEnd >>= 1;
 108                mt->VTotal >>= 1;
 109        }
 110        if ((mt->HTotal & 7) == 2) {
 111                datactl |= 0x00000010;
 112                mt->HTotal &= ~7;
 113        }
 114        tmp |= 0x10000000;      /* 0x10000000 is VIDRST polarity */
 115        mga_outl(0x3C14, ((mt->HDisplay - 8) << 16) | (mt->HTotal - 8));
 116        mga_outl(0x3C18, ((mt->HSyncEnd - 8) << 16) | (mt->HSyncStart - 8));
 117        mga_outl(0x3C1C, ((mt->VDisplay - 1) << 16) | (mt->VTotal - 1));
 118        mga_outl(0x3C20, ((mt->VSyncEnd - 1) << 16) | (mt->VSyncStart - 1));
 119        mga_outl(0x3C24, ((mt->VSyncStart) << 16) | (mt->HSyncStart));  /* preload */
 120        {
 121                u_int32_t linelen = m2info->fbcon.var.xres_virtual * (m2info->fbcon.var.bits_per_pixel >> 3);
 122                if (tmp & 0x02000000) {
 123                        /* field #0 is smaller, so... */
 124                        mga_outl(0x3C2C, pos);                  /* field #1 vmemory start */
 125                        mga_outl(0x3C28, pos + linelen);        /* field #0 vmemory start */
 126                        linelen <<= 1;
 127                        m2info->interlaced = 1;
 128                } else {
 129                        mga_outl(0x3C28, pos);          /* vmemory start */
 130                        m2info->interlaced = 0;
 131                }
 132                mga_outl(0x3C40, linelen);
 133        }
 134        mga_outl(0x3C4C, datactl);      /* data control */
 135        if (tmp & 0x02000000) {
 136                int i;
 137
 138                mga_outl(0x3C10, tmp & ~0x02000000);
 139                for (i = 0; i < 2; i++) {
 140                        unsigned int nl;
 141                        unsigned int lastl = 0;
 142
 143                        while ((nl = mga_inl(0x3C48) & 0xFFF) >= lastl) {
 144                                lastl = nl;
 145                        }
 146                }
 147        }
 148        mga_outl(0x3C10, tmp);
 149        minfo->hw.crtc2.ctl = tmp;
 150
 151        tmp = mt->VDisplay << 16;       /* line compare */
 152        if (mt->sync & FB_SYNC_HOR_HIGH_ACT)
 153                tmp |= 0x00000100;
 154        if (mt->sync & FB_SYNC_VERT_HIGH_ACT)
 155                tmp |= 0x00000200;
 156        mga_outl(0x3C44, tmp);
 157}
 158
 159static void matroxfb_dh_disable(struct matroxfb_dh_fb_info* m2info) {
 160        struct matrox_fb_info *minfo = m2info->primary_dev;
 161
 162        mga_outl(0x3C10, 0x00000004);   /* disable CRTC2, CRTC1->DAC1, PLL as clock source */
 163        minfo->hw.crtc2.ctl = 0x00000004;
 164}
 165
 166static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info,
 167                struct fb_var_screeninfo* var) {
 168        unsigned int pos;
 169        unsigned int linelen;
 170        unsigned int pixelsize;
 171        struct matrox_fb_info *minfo = m2info->primary_dev;
 172
 173        m2info->fbcon.var.xoffset = var->xoffset;
 174        m2info->fbcon.var.yoffset = var->yoffset;
 175        pixelsize = m2info->fbcon.var.bits_per_pixel >> 3;
 176        linelen = m2info->fbcon.var.xres_virtual * pixelsize;
 177        pos = m2info->fbcon.var.yoffset * linelen + m2info->fbcon.var.xoffset * pixelsize;
 178        pos += m2info->video.offbase;
 179        if (m2info->interlaced) {
 180                mga_outl(0x3C2C, pos);
 181                mga_outl(0x3C28, pos + linelen);
 182        } else {
 183                mga_outl(0x3C28, pos);
 184        }
 185}
 186
 187static int matroxfb_dh_decode_var(struct matroxfb_dh_fb_info* m2info,
 188                struct fb_var_screeninfo* var,
 189                int *visual,
 190                int *video_cmap_len,
 191                int *mode) {
 192        unsigned int mask;
 193        unsigned int memlen;
 194        unsigned int vramlen;
 195
 196        switch (var->bits_per_pixel) {
 197                case 16:        mask = 0x1F;
 198                                break;
 199                case 32:        mask = 0x0F;
 200                                break;
 201                default:        return -EINVAL;
 202        }
 203        vramlen = m2info->video.len_usable;
 204        if (var->yres_virtual < var->yres)
 205                var->yres_virtual = var->yres;
 206        if (var->xres_virtual < var->xres)
 207                var->xres_virtual = var->xres;
 208        var->xres_virtual = (var->xres_virtual + mask) & ~mask;
 209        if (var->yres_virtual > 32767)
 210                return -EINVAL;
 211        memlen = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel >> 3);
 212        if (memlen > vramlen)
 213                return -EINVAL;
 214        if (var->xoffset + var->xres > var->xres_virtual)
 215                var->xoffset = var->xres_virtual - var->xres;
 216        if (var->yoffset + var->yres > var->yres_virtual)
 217                var->yoffset = var->yres_virtual - var->yres;
 218
 219        var->xres &= ~7;
 220        var->left_margin &= ~7;
 221        var->right_margin &= ~7;
 222        var->hsync_len &= ~7;
 223
 224        *mode = var->bits_per_pixel;
 225        if (var->bits_per_pixel == 16) {
 226                if (var->green.length == 5) {
 227                        var->red.offset = 10;
 228                        var->red.length = 5;
 229                        var->green.offset = 5;
 230                        var->green.length = 5;
 231                        var->blue.offset = 0;
 232                        var->blue.length = 5;
 233                        var->transp.offset = 15;
 234                        var->transp.length = 1;
 235                        *mode = 15;
 236                } else {
 237                        var->red.offset = 11;
 238                        var->red.length = 5;
 239                        var->green.offset = 5;
 240                        var->green.length = 6;
 241                        var->blue.offset = 0;
 242                        var->blue.length = 5;
 243                        var->transp.offset = 0;
 244                        var->transp.length = 0;
 245                }
 246        } else {
 247                        var->red.offset = 16;
 248                        var->red.length = 8;
 249                        var->green.offset = 8;
 250                        var->green.length = 8;
 251                        var->blue.offset = 0;
 252                        var->blue.length = 8;
 253                        var->transp.offset = 24;
 254                        var->transp.length = 8;
 255        }
 256        *visual = FB_VISUAL_TRUECOLOR;
 257        *video_cmap_len = 16;
 258        return 0;
 259}
 260
 261static int matroxfb_dh_open(struct fb_info* info, int user) {
 262#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
 263        struct matrox_fb_info *minfo = m2info->primary_dev;
 264
 265        if (minfo) {
 266                int err;
 267
 268                if (minfo->dead) {
 269                        return -ENXIO;
 270                }
 271                err = minfo->fbops.fb_open(&minfo->fbcon, user);
 272                if (err) {
 273                        return err;
 274                }
 275        }
 276        return 0;
 277#undef m2info
 278}
 279
 280static int matroxfb_dh_release(struct fb_info* info, int user) {
 281#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
 282        int err = 0;
 283        struct matrox_fb_info *minfo = m2info->primary_dev;
 284
 285        if (minfo) {
 286                err = minfo->fbops.fb_release(&minfo->fbcon, user);
 287        }
 288        return err;
 289#undef m2info
 290}
 291
 292/*
 293 * This function is called before the register_framebuffer so
 294 * no locking is needed.
 295 */
 296static void matroxfb_dh_init_fix(struct matroxfb_dh_fb_info *m2info)
 297{
 298        struct fb_fix_screeninfo *fix = &m2info->fbcon.fix;
 299
 300        strcpy(fix->id, "MATROX DH");
 301
 302        fix->smem_start = m2info->video.base;
 303        fix->smem_len = m2info->video.len_usable;
 304        fix->ypanstep = 1;
 305        fix->ywrapstep = 0;
 306        fix->xpanstep = 8;      /* TBD */
 307        fix->mmio_start = m2info->mmio.base;
 308        fix->mmio_len = m2info->mmio.len;
 309        fix->accel = 0;         /* no accel... */
 310}
 311
 312static int matroxfb_dh_check_var(struct fb_var_screeninfo* var, struct fb_info* info) {
 313#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
 314        int visual;
 315        int cmap_len;
 316        int mode;
 317
 318        return matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode);
 319#undef m2info
 320}
 321
 322static int matroxfb_dh_set_par(struct fb_info* info) {
 323#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
 324        int visual;
 325        int cmap_len;
 326        int mode;
 327        int err;
 328        struct fb_var_screeninfo* var = &info->var;
 329        struct matrox_fb_info *minfo = m2info->primary_dev;
 330
 331        if ((err = matroxfb_dh_decode_var(m2info, var, &visual, &cmap_len, &mode)) != 0)
 332                return err;
 333        /* cmap */
 334        {
 335                m2info->fbcon.screen_base = vaddr_va(m2info->video.vbase);
 336                m2info->fbcon.fix.visual = visual;
 337                m2info->fbcon.fix.type = FB_TYPE_PACKED_PIXELS;
 338                m2info->fbcon.fix.type_aux = 0;
 339                m2info->fbcon.fix.line_length = (var->xres_virtual * var->bits_per_pixel) >> 3;
 340        }
 341        {
 342                struct my_timming mt;
 343                unsigned int pos;
 344                int out;
 345                int cnt;
 346
 347                matroxfb_var2my(&m2info->fbcon.var, &mt);
 348                mt.crtc = MATROXFB_SRC_CRTC2;
 349                /* CRTC2 delay */
 350                mt.delay = 34;
 351
 352                pos = (m2info->fbcon.var.yoffset * m2info->fbcon.var.xres_virtual + m2info->fbcon.var.xoffset) * m2info->fbcon.var.bits_per_pixel >> 3;
 353                pos += m2info->video.offbase;
 354                cnt = 0;
 355                down_read(&minfo->altout.lock);
 356                for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
 357                        if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) {
 358                                cnt++;
 359                                if (minfo->outputs[out].output->compute) {
 360                                        minfo->outputs[out].output->compute(minfo->outputs[out].data, &mt);
 361                                }
 362                        }
 363                }
 364                minfo->crtc2.pixclock = mt.pixclock;
 365                minfo->crtc2.mnp = mt.mnp;
 366                up_read(&minfo->altout.lock);
 367                if (cnt) {
 368                        matroxfb_dh_restore(m2info, &mt, mode, pos);
 369                } else {
 370                        matroxfb_dh_disable(m2info);
 371                }
 372                DAC1064_global_init(minfo);
 373                DAC1064_global_restore(minfo);
 374                down_read(&minfo->altout.lock);
 375                for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
 376                        if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2 &&
 377                            minfo->outputs[out].output->program) {
 378                                minfo->outputs[out].output->program(minfo->outputs[out].data);
 379                        }
 380                }
 381                for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
 382                        if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2 &&
 383                            minfo->outputs[out].output->start) {
 384                                minfo->outputs[out].output->start(minfo->outputs[out].data);
 385                        }
 386                }
 387                up_read(&minfo->altout.lock);
 388        }
 389        m2info->initialized = 1;
 390        return 0;
 391#undef m2info
 392}
 393
 394static int matroxfb_dh_pan_display(struct fb_var_screeninfo* var, struct fb_info* info) {
 395#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
 396        matroxfb_dh_pan_var(m2info, var);
 397        return 0;
 398#undef m2info
 399}
 400
 401static int matroxfb_dh_get_vblank(const struct matroxfb_dh_fb_info* m2info, struct fb_vblank* vblank) {
 402        struct matrox_fb_info *minfo = m2info->primary_dev;
 403
 404        matroxfb_enable_irq(minfo, 0);
 405        memset(vblank, 0, sizeof(*vblank));
 406        vblank->flags = FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_VBLANK;
 407        /* mask out reserved bits + field number (odd/even) */
 408        vblank->vcount = mga_inl(0x3C48) & 0x000007FF;
 409        /* compatibility stuff */
 410        if (vblank->vcount >= m2info->fbcon.var.yres)
 411                vblank->flags |= FB_VBLANK_VBLANKING;
 412        if (test_bit(0, &minfo->irq_flags)) {
 413                vblank->flags |= FB_VBLANK_HAVE_COUNT;
 414                /* Only one writer, aligned int value...
 415                   it should work without lock and without atomic_t */
 416                vblank->count = minfo->crtc2.vsync.cnt;
 417        }
 418        return 0;
 419}
 420
 421static int matroxfb_dh_ioctl(struct fb_info *info,
 422                unsigned int cmd,
 423                unsigned long arg)
 424{
 425#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
 426        struct matrox_fb_info *minfo = m2info->primary_dev;
 427
 428        DBG(__func__)
 429
 430        switch (cmd) {
 431                case FBIOGET_VBLANK:
 432                        {
 433                                struct fb_vblank vblank;
 434                                int err;
 435
 436                                err = matroxfb_dh_get_vblank(m2info, &vblank);
 437                                if (err)
 438                                        return err;
 439                                if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank)))
 440                                        return -EFAULT;
 441                                return 0;
 442                        }
 443                case FBIO_WAITFORVSYNC:
 444                        {
 445                                u_int32_t crt;
 446
 447                                if (get_user(crt, (u_int32_t __user *)arg))
 448                                        return -EFAULT;
 449
 450                                if (crt != 0)
 451                                        return -ENODEV;
 452                                return matroxfb_wait_for_sync(minfo, 1);
 453                        }
 454                case MATROXFB_SET_OUTPUT_MODE:
 455                case MATROXFB_GET_OUTPUT_MODE:
 456                case MATROXFB_GET_ALL_OUTPUTS:
 457                        {
 458                                return minfo->fbcon.fbops->fb_ioctl(&minfo->fbcon, cmd, arg);
 459                        }
 460                case MATROXFB_SET_OUTPUT_CONNECTION:
 461                        {
 462                                u_int32_t tmp;
 463                                int out;
 464                                int changes;
 465
 466                                if (get_user(tmp, (u_int32_t __user *)arg))
 467                                        return -EFAULT;
 468                                for (out = 0; out < 32; out++) {
 469                                        if (tmp & (1 << out)) {
 470                                                if (out >= MATROXFB_MAX_OUTPUTS)
 471                                                        return -ENXIO;
 472                                                if (!minfo->outputs[out].output)
 473                                                        return -ENXIO;
 474                                                switch (minfo->outputs[out].src) {
 475                                                        case MATROXFB_SRC_NONE:
 476                                                        case MATROXFB_SRC_CRTC2:
 477                                                                break;
 478                                                        default:
 479                                                                return -EBUSY;
 480                                                }
 481                                        }
 482                                }
 483                                if (minfo->devflags.panellink) {
 484                                        if (tmp & MATROXFB_OUTPUT_CONN_DFP)
 485                                                return -EINVAL;
 486                                        if ((minfo->outputs[2].src == MATROXFB_SRC_CRTC1) && tmp)
 487                                                return -EBUSY;
 488                                }
 489                                changes = 0;
 490                                for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
 491                                        if (tmp & (1 << out)) {
 492                                                if (minfo->outputs[out].src != MATROXFB_SRC_CRTC2) {
 493                                                        changes = 1;
 494                                                        minfo->outputs[out].src = MATROXFB_SRC_CRTC2;
 495                                                }
 496                                        } else if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) {
 497                                                changes = 1;
 498                                                minfo->outputs[out].src = MATROXFB_SRC_NONE;
 499                                        }
 500                                }
 501                                if (!changes)
 502                                        return 0;
 503                                matroxfb_dh_set_par(info);
 504                                return 0;
 505                        }
 506                case MATROXFB_GET_OUTPUT_CONNECTION:
 507                        {
 508                                u_int32_t conn = 0;
 509                                int out;
 510
 511                                for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
 512                                        if (minfo->outputs[out].src == MATROXFB_SRC_CRTC2) {
 513                                                conn |= 1 << out;
 514                                        }
 515                                }
 516                                if (put_user(conn, (u_int32_t __user *)arg))
 517                                        return -EFAULT;
 518                                return 0;
 519                        }
 520                case MATROXFB_GET_AVAILABLE_OUTPUTS:
 521                        {
 522                                u_int32_t tmp = 0;
 523                                int out;
 524
 525                                for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
 526                                        if (minfo->outputs[out].output) {
 527                                                switch (minfo->outputs[out].src) {
 528                                                        case MATROXFB_SRC_NONE:
 529                                                        case MATROXFB_SRC_CRTC2:
 530                                                                tmp |= 1 << out;
 531                                                                break;
 532                                                }
 533                                        }
 534                                }
 535                                if (minfo->devflags.panellink) {
 536                                        tmp &= ~MATROXFB_OUTPUT_CONN_DFP;
 537                                        if (minfo->outputs[2].src == MATROXFB_SRC_CRTC1) {
 538                                                tmp = 0;
 539                                        }
 540                                }
 541                                if (put_user(tmp, (u_int32_t __user *)arg))
 542                                        return -EFAULT;
 543                                return 0;
 544                        }
 545        }
 546        return -ENOTTY;
 547#undef m2info
 548}
 549
 550static int matroxfb_dh_blank(int blank, struct fb_info* info) {
 551#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon))
 552        switch (blank) {
 553                case 1:
 554                case 2:
 555                case 3:
 556                case 4:
 557                default:;
 558        }
 559        /* do something... */
 560        return 0;
 561#undef m2info
 562}
 563
 564static struct fb_ops matroxfb_dh_ops = {
 565        .owner =        THIS_MODULE,
 566        .fb_open =      matroxfb_dh_open,
 567        .fb_release =   matroxfb_dh_release,
 568        .fb_check_var = matroxfb_dh_check_var,
 569        .fb_set_par =   matroxfb_dh_set_par,
 570        .fb_setcolreg = matroxfb_dh_setcolreg,
 571        .fb_pan_display =matroxfb_dh_pan_display,
 572        .fb_blank =     matroxfb_dh_blank,
 573        .fb_ioctl =     matroxfb_dh_ioctl,
 574        .fb_fillrect =  cfb_fillrect,
 575        .fb_copyarea =  cfb_copyarea,
 576        .fb_imageblit = cfb_imageblit,
 577};
 578
 579static struct fb_var_screeninfo matroxfb_dh_defined = {
 580                640,480,640,480,/* W,H, virtual W,H */
 581                0,0,            /* offset */
 582                32,             /* depth */
 583                0,              /* gray */
 584                {0,0,0},        /* R */
 585                {0,0,0},        /* G */
 586                {0,0,0},        /* B */
 587                {0,0,0},        /* alpha */
 588                0,              /* nonstd */
 589                FB_ACTIVATE_NOW,
 590                -1,-1,          /* display size */
 591                0,              /* accel flags */
 592                39721L,48L,16L,33L,10L,
 593                96L,2,0,        /* no sync info */
 594                FB_VMODE_NONINTERLACED,
 595                0, {0,0,0,0,0}
 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