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 <asm/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 MINFO_FROM(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 (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC2) { 85 if (ACCESS_FBINFO(devflags.g450dac)) { 86 tmp |= 0x00000006; /* source from secondary pixel PLL */ 87 /* no vidrst when in monitor mode */ 88 if (ACCESS_FBINFO(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 (ACCESS_FBINFO(outputs[0]).src == MATROXFB_SRC_CRTC2) { 97 tmp |= 0x00000004; /* source from pixclock */ 98 /* PIXPLL is our clock source */ 99 } 100 if (ACCESS_FBINFO(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 ACCESS_FBINFO(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 MINFO_FROM(m2info->primary_dev); 161 162 mga_outl(0x3C10, 0x00000004); /* disable CRTC2, CRTC1->DAC1, PLL as clock source */ 163 ACCESS_FBINFO(hw).crtc2.ctl = 0x00000004; 164} 165 166static void matroxfb_dh_cfbX_init(struct matroxfb_dh_fb_info* m2info) { 167 /* no acceleration for secondary head... */ 168 m2info->cmap[16] = 0xFFFFFFFF; 169} 170 171static void matroxfb_dh_pan_var(struct matroxfb_dh_fb_info* m2info, 172 struct fb_var_screeninfo* var) { 173 unsigned int pos; 174 unsigned int linelen; 175 unsigned int pixelsize; 176 MINFO_FROM(m2info->primary_dev); 177 178 m2info->fbcon.var.xoffset = var->xoffset; 179 m2info->fbcon.var.yoffset = var->yoffset; 180 pixelsize = m2info->fbcon.var.bits_per_pixel >> 3; 181 linelen = m2info->fbcon.var.xres_virtual * pixelsize; 182 pos = m2info->fbcon.var.yoffset * linelen + m2info->fbcon.var.xoffset * pixelsize; 183 pos += m2info->video.offbase; 184 if (m2info->interlaced) { 185 mga_outl(0x3C2C, pos); 186 mga_outl(0x3C28, pos + linelen); 187 } else { 188 mga_outl(0x3C28, pos); 189 } 190} 191 192static int matroxfb_dh_decode_var(struct matroxfb_dh_fb_info* m2info, 193 struct fb_var_screeninfo* var, 194 int *visual, 195 int *video_cmap_len, 196 int *mode) { 197 unsigned int mask; 198 unsigned int memlen; 199 unsigned int vramlen; 200 201 switch (var->bits_per_pixel) { 202 case 16: mask = 0x1F; 203 break; 204 case 32: mask = 0x0F; 205 break; 206 default: return -EINVAL; 207 } 208 vramlen = m2info->video.len_usable; 209 if (var->yres_virtual < var->yres) 210 var->yres_virtual = var->yres; 211 if (var->xres_virtual < var->xres) 212 var->xres_virtual = var->xres; 213 var->xres_virtual = (var->xres_virtual + mask) & ~mask; 214 if (var->yres_virtual > 32767) 215 return -EINVAL; 216 memlen = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel >> 3); 217 if (memlen > vramlen) 218 return -EINVAL; 219 if (var->xoffset + var->xres > var->xres_virtual) 220 var->xoffset = var->xres_virtual - var->xres; 221 if (var->yoffset + var->yres > var->yres_virtual) 222 var->yoffset = var->yres_virtual - var->yres; 223 224 var->xres &= ~7; 225 var->left_margin &= ~7; 226 var->right_margin &= ~7; 227 var->hsync_len &= ~7; 228 229 *mode = var->bits_per_pixel; 230 if (var->bits_per_pixel == 16) { 231 if (var->green.length == 5) { 232 var->red.offset = 10; 233 var->red.length = 5; 234 var->green.offset = 5; 235 var->green.length = 5; 236 var->blue.offset = 0; 237 var->blue.length = 5; 238 var->transp.offset = 15; 239 var->transp.length = 1; 240 *mode = 15; 241 } else { 242 var->red.offset = 11; 243 var->red.length = 5; 244 var->green.offset = 5; 245 var->green.length = 6; 246 var->blue.offset = 0; 247 var->blue.length = 5; 248 var->transp.offset = 0; 249 var->transp.length = 0; 250 } 251 } else { 252 var->red.offset = 16; 253 var->red.length = 8; 254 var->green.offset = 8; 255 var->green.length = 8; 256 var->blue.offset = 0; 257 var->blue.length = 8; 258 var->transp.offset = 24; 259 var->transp.length = 8; 260 } 261 *visual = FB_VISUAL_TRUECOLOR; 262 *video_cmap_len = 16; 263 return 0; 264} 265 266static int matroxfb_dh_open(struct fb_info* info, int user) { 267#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 268 MINFO_FROM(m2info->primary_dev); 269 270 if (MINFO) { 271 int err; 272 273 if (ACCESS_FBINFO(dead)) { 274 return -ENXIO; 275 } 276 err = ACCESS_FBINFO(fbops).fb_open(&ACCESS_FBINFO(fbcon), user); 277 if (err) { 278 return err; 279 } 280 } 281 return 0; 282#undef m2info 283} 284 285static int matroxfb_dh_release(struct fb_info* info, int user) { 286#define m2info (container_of(info, struct matroxfb_dh_fb_info, fbcon)) 287 int err = 0; 288 MINFO_FROM(m2info->primary_dev); 289 290 if (MINFO) { 291 err = ACCESS_FBINFO(fbops).fb_release(&ACCESS_FBINFO(fbcon), user); 292 } 293 return err; 294#undef m2info 295} 296 297static void matroxfb_dh_init_fix(struct matroxfb_dh_fb_info *m2info) { 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 MINFO_FROM(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(&ACCESS_FBINFO(altout).lock); 356 for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { 357 if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) { 358 cnt++; 359 if (ACCESS_FBINFO(outputs[out]).output->compute) { 360 ACCESS_FBINFO(outputs[out]).output->compute(ACCESS_FBINFO(outputs[out]).data, &mt); 361 } 362 } 363 } 364 ACCESS_FBINFO(crtc2).pixclock = mt.pixclock; 365 ACCESS_FBINFO(crtc2).mnp = mt.mnp; 366 up_read(&ACCESS_FBINFO(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(PMINFO2); 373 DAC1064_global_restore(PMINFO2); 374 down_read(&ACCESS_FBINFO(altout).lock); 375 for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { 376 if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2 && 377 ACCESS_FBINFO(outputs[out]).output->program) { 378 ACCESS_FBINFO(outputs[out]).output->program(ACCESS_FBINFO(outputs[out]).data); 379 } 380 } 381 for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) { 382 if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2 && 383 ACCESS_FBINFO(outputs[out]).output->start) { 384 ACCESS_FBINFO(outputs[out]).output->start(ACCESS_FBINFO(outputs[out]).data); 385 } 386 } 387 up_read(&ACCESS_FBINFO(altout).lock); 388 matroxfb_dh_cfbX_init(m2info); 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 MINFO_FROM(m2info->primary_dev); 404 405 matroxfb_enable_irq(PMINFO 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, &ACCESS_FBINFO(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 = ACCESS_FBINFO(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 MINFO_FROM(m2info->primary_dev); 428 429 DBG(__FUNCTION__) 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(PMINFO 1); 454 } 455 case MATROXFB_SET_OUTPUT_MODE: 456 case MATROXFB_GET_OUTPUT_MODE: 457 case MATROXFB_GET_ALL_OUTPUTS: 458 { 459 return ACCESS_FBINFO(fbcon.fbops)->fb_ioctl(&ACCESS_FBINFO(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 (!ACCESS_FBINFO(outputs[out]).output) 474 return -ENXIO; 475 switch (ACCESS_FBINFO(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 (ACCESS_FBINFO(devflags.panellink)) { 485 if (tmp & MATROXFB_OUTPUT_CONN_DFP) 486 return -EINVAL; 487 if ((ACCESS_FBINFO(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 (ACCESS_FBINFO(outputs[out]).src != MATROXFB_SRC_CRTC2) { 494 changes = 1; 495 ACCESS_FBINFO(outputs[out]).src = MATROXFB_SRC_CRTC2; 496 } 497 } else if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) { 498 changes = 1; 499 ACCESS_FBINFO(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 (ACCESS_FBINFO(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 (ACCESS_FBINFO(outputs[out]).output) { 528 switch (ACCESS_FBINFO(outputs[out]).src) { 529 case MATROXFB_SRC_NONE: 530 case MATROXFB_SRC_CRTC2: 531 tmp |= 1 << out; 532 break; 533 } 534 } 535 } 536 if (ACCESS_FBINFO(devflags.panellink)) { 537 tmp &= ~MATROXFB_OUTPUT_CONN_DFP; 538 if (ACCESS_FBINFO(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 0, {0,0,0,0,0} 597}; 598 599static int matroxfb_dh_regit(CPMINFO struct matroxfb_dh_fb_info* m2info) { 600#define minfo (m2info->primary_dev) 601 void* oldcrtc2; 602 603 m2info->fbcon.fbops = &matroxfb_dh_ops; 604 m2info->fbcon.flags = FBINFO_FLAG_DEFAULT; 605 m2info->fbcon.flags |= FBINFO_HWACCEL_XPAN | 606 FBINFO_HWACCEL_YPAN; 607 m2info->fbcon.pseudo_palette = m2info->cmap; 608 fb_alloc_cmap(&m2info->fbcon.cmap, 256, 1); 609 610 if (mem < 64) 611 mem *= 1024; 612 if (mem < 64*1024) 613 mem *= 1024; 614 mem &= ~0x00000FFF; /* PAGE_MASK? */ 615 if (ACCESS_FBINFO(video.len_usable) + mem <= ACCESS_FBINFO(video.len)) 616 m2info->video.offbase = ACCESS_FBINFO(video.len) - mem; 617 else if (ACCESS_FBINFO(video.len) < mem) { 618 return -ENOMEM; 619 } else { /* check yres on first head... */ 620 m2info->video.borrowed = mem; 621 ACCESS_FBINFO(video.len_usable) -= mem; 622 m2info->video.offbase = ACCESS_FBINFO(video.len_usable); 623 } 624 m2info->video.base = ACCESS_FBINFO(video.base) + m2info->video.offbase; 625 m2info->video.len = m2info->video.len_usable = m2info->video.len_maximum = mem; 626 m2info->video.vbase.vaddr = vaddr_va(ACCESS_FBINFO(video.vbase)) + m2info->video.offbase; 627 m2info->mmio.base = ACCESS_FBINFO(mmio.base); 628 m2info->mmio.vbase = ACCESS_FBINFO(mmio.vbase); 629 m2info->mmio.len = ACCESS_FBINFO(mmio.len); 630 631 matroxfb_dh_init_fix(m2info); 632 if (register_framebuffer(&m2info->fbcon)) { 633 return -ENXIO; 634 } 635 if (!m2info->initialized) 636 fb_set_var(&m2info->fbcon, &matroxfb_dh_defined); 637 down_write(&ACCESS_FBINFO(crtc2.lock)); 638 oldcrtc2 = ACCESS_FBINFO(crtc2.info); 639 ACCESS_FBINFO(crtc2.info) = m2info; 640 up_write(&ACCESS_FBINFO(crtc2.lock)); 641 if (oldcrtc2) { 642 printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 already present: %p\n", 643 oldcrtc2); 644 } 645 return 0; 646#undef minfo 647} 648 649/* ************************** */ 650 651static int matroxfb_dh_registerfb(struct matroxfb_dh_fb_info* m2info) { 652#define minfo (m2info->primary_dev) 653 if (matroxfb_dh_regit(PMINFO m2info)) { 654 printk(KERN_ERR "matroxfb_crtc2: secondary head failed to register\n"); 655 return -1; 656 } 657 printk(KERN_INFO "matroxfb_crtc2: secondary head of fb%u was registered as fb%u\n", 658 ACCESS_FBINFO(fbcon.node), m2info->fbcon.node); 659 m2info->fbcon_registered = 1; 660 return 0; 661#undef minfo 662} 663 664static void matroxfb_dh_deregisterfb(struct matroxfb_dh_fb_info* m2info) { 665#define minfo (m2info->primary_dev) 666 if (m2info->fbcon_registered) { 667 int id; 668 struct matroxfb_dh_fb_info* crtc2; 669 670 down_write(&ACCESS_FBINFO(crtc2.lock)); 671 crtc2 = ACCESS_FBINFO(crtc2.info); 672 if (crtc2 == m2info) 673 ACCESS_FBINFO(crtc2.info) = NULL; 674 up_write(&ACCESS_FBINFO(crtc2.lock)); 675 if (crtc2 != m2info) { 676 printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 mismatch at unload: %p != %p\n", 677 crtc2, m2info); 678 printk(KERN_ERR "matroxfb_crtc2: Expect kernel crash after module unload.\n"); 679 return; 680 } 681 id = m2info->fbcon.node; 682 unregister_framebuffer(&m2info->fbcon); 683 /* return memory back to primary head */ 684 ACCESS_FBINFO(video.len_usable) += m2info->video.borrowed; 685 printk(KERN_INFO "matroxfb_crtc2: fb%u unregistered\n", id); 686 m2info->fbcon_registered = 0; 687 } 688#undef minfo 689} 690 691static void* matroxfb_crtc2_probe(struct matrox_fb_info* minfo) { 692 struct matroxfb_dh_fb_info* m2info; 693 694 /* hardware is CRTC2 incapable... */ 695 if (!ACCESS_FBINFO(devflags.crtc2)) 696 return NULL; 697 m2info = kzalloc(sizeof(*m2info), GFP_KERNEL); 698 if (!m2info) { 699 printk(KERN_ERR "matroxfb_crtc2: Not enough memory for CRTC2 control structs\n"); 700 return NULL; 701 } 702 m2info->primary_dev = MINFO; 703 if (matroxfb_dh_registerfb(m2info)) { 704 kfree(m2info); 705 printk(KERN_ERR "matroxfb_crtc2: CRTC2 framebuffer failed to register\n"); 706 return NULL; 707 } 708 return m2info; 709} 710 711static void matroxfb_crtc2_remove(struct matrox_fb_info* minfo, void* crtc2) { 712 matroxfb_dh_deregisterfb(crtc2); 713 kfree(crtc2); 714} 715 716static struct matroxfb_driver crtc2 = { 717 .name = "Matrox G400 CRTC2", 718 .probe = matroxfb_crtc2_probe, 719 .remove = matroxfb_crtc2_remove }; 720 721static int matroxfb_crtc2_init(void) { 722 if (fb_get_options("matrox_crtc2fb", NULL)) 723 return -ENODEV; 724 725 matroxfb_register_driver(&crtc2); 726 return 0; 727} 728 729static void matroxfb_crtc2_exit(void) { 730 matroxfb_unregister_driver(&crtc2); 731} 732 733MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>"); 734MODULE_DESCRIPTION("Matrox G400 CRTC2 driver"); 735MODULE_LICENSE("GPL"); 736module_init(matroxfb_crtc2_init); 737module_exit(matroxfb_crtc2_exit); 738/* we do not have __setup() yet */ 739