1 /* 2 Copyright 1999, Be Incorporated. All Rights Reserved. 3 This file may be used under the terms of the Be Sample Code License. 4 5 Other authors: 6 Mark Watson, 7 Apsed, 8 Rudolf Cornelissen 11/2002-1/2006 9 */ 10 11 #define MODULE_BIT 0x00200000 12 13 #include "acc_std.h" 14 15 /* 16 Enable/Disable interrupts. Just a wrapper around the 17 ioctl() to the kernel driver. 18 */ 19 static void interrupt_enable(bool flag) 20 { 21 status_t result; 22 gx00_set_bool_state sbs; 23 24 if (si->ps.int_assigned) 25 { 26 /* set the magic number so the driver knows we're for real */ 27 sbs.magic = GX00_PRIVATE_DATA_MAGIC; 28 sbs.do_it = flag; 29 /* contact driver and get a pointer to the registers and shared data */ 30 result = ioctl(fd, GX00_RUN_INTERRUPTS, &sbs, sizeof(sbs)); 31 } 32 } 33 34 /* First validate the mode, then call lots of bit banging stuff to set the mode(s)! */ 35 status_t SET_DISPLAY_MODE(display_mode *mode_to_set) 36 { 37 /* BOUNDS WARNING: 38 * It's impossible to deviate whatever small amount in a display_mode if the lower 39 * and upper limits are the same! 40 * Besides: 41 * BeOS (tested R5.0.3PE) is failing BWindowScreen::SetFrameBuffer() if PROPOSEMODE 42 * returns B_BAD_VALUE! 43 * Which means PROPOSEMODE should not return that on anything except on 44 * deviations for: 45 * display_mode.virtual_width; 46 * display_mode.virtual_height; 47 * display_mode.timing.h_display; 48 * display_mode.timing.v_display; 49 * So: 50 * We don't use bounds here by making sure bounds and target are the same struct! 51 * (See the call to PROPOSE_DISPLAY_MODE below) */ 52 display_mode /*bounds,*/ target; 53 54 uint8 colour_depth1 = 32; 55 status_t result; 56 uint32 startadd,startadd_right; 57 58 /* Adjust mode to valid one and fail if invalid */ 59 target /*= bounds*/ = *mode_to_set; 60 /* show the mode bits */ 61 LOG(1, ("SETMODE: (ENTER) initial modeflags: $%08x\n", target.flags)); 62 LOG(1, ("SETMODE: requested target pixelclock %dkHz\n", target.timing.pixel_clock)); 63 LOG(1, ("SETMODE: requested virtual_width %d, virtual_height %d\n", 64 target.virtual_width, target.virtual_height)); 65 66 /* See BOUNDS WARNING above... */ 67 if (PROPOSE_DISPLAY_MODE(&target, &target, &target) == B_ERROR) return B_ERROR; 68 69 /* overlay engine, cursor and MOVE_DISPLAY need to know the status even when 70 * in singlehead mode */ 71 si->switched_crtcs = false; 72 73 /* disable interrupts using the kernel driver */ 74 interrupt_enable(false); 75 76 /* then turn off screen(s) */ 77 gx00_crtc_dpms(false, false, false); 78 if (si->ps.secondary_head) g400_crtc2_dpms(false, false, false); 79 80 /*where in framebuffer the screen is (should this be dependant on previous MOVEDISPLAY?)*/ 81 startadd = (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer; 82 83 /* calculate and set new mode bytes_per_row */ 84 gx00_general_validate_pic_size (&target, &si->fbc.bytes_per_row, &si->acc_mode); 85 86 /*Perform the very long mode switch!*/ 87 if (target.flags & DUALHEAD_BITS) /*if some dualhead mode*/ 88 { 89 uint8 colour_depth2 = colour_depth1; 90 91 /* init display mode for secondary head */ 92 display_mode target2 = target; 93 94 LOG(1,("SETMODE: setting DUALHEAD mode\n")); 95 96 /* set the pixel clock PLL(s) */ 97 LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock)); 98 if (gx00_dac_set_pix_pll(target) == B_ERROR) 99 LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n")); 100 101 /* we do not need to set the pixelclock here for a head that's in TVout mode */ 102 if (!(target2.flags & TV_BITS)) 103 { 104 LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock)); 105 if (gx00_maven_set_vid_pll(target2) == B_ERROR) 106 LOG(8,("SETMODE: error setting pixel clock (MAVEN)\n")); 107 } 108 109 /*set the colour depth for CRTC1 and the DAC */ 110 switch(target.space) 111 { 112 case B_RGB16_LITTLE: 113 colour_depth1 = 16; 114 gx00_dac_mode(BPP16, 1.0); 115 gx00_crtc_depth(BPP16); 116 break; 117 case B_RGB32_LITTLE: 118 colour_depth1 = 32; 119 gx00_dac_mode(BPP32, 1.0); 120 gx00_crtc_depth(BPP32); 121 break; 122 } 123 /*set the colour depth for CRTC2 and the MAVEN */ 124 switch(target2.space) 125 { 126 case B_RGB16_LITTLE: 127 colour_depth2 = 16; 128 gx00_maven_mode(BPP16, 1.0); 129 g400_crtc2_depth(BPP16); 130 break; 131 case B_RGB32_LITTLE: 132 colour_depth2 = 32; 133 gx00_maven_mode(BPP32DIR, 1.0); 134 g400_crtc2_depth(BPP32DIR); 135 break; 136 } 137 138 /* check if we are doing interlaced TVout mode */ 139 si->interlaced_tv_mode = false; 140 if ((target2.flags & TV_BITS) && (si->ps.card_type >= G450)) 141 si->interlaced_tv_mode = true; 142 143 /*set the display(s) pitches*/ 144 gx00_crtc_set_display_pitch (); 145 //fixme: seperate for real dualhead modes: 146 //we need a secondary si->fbc! 147 g400_crtc2_set_display_pitch (); 148 149 /*work out where the "right" screen starts*/ 150 startadd_right=startadd+(target.timing.h_display * (colour_depth1 >> 3)); 151 152 /* calculate needed MAVEN-CRTC delay: formula valid for straight-through CRTC's */ 153 si->crtc_delay = 44 + 0 * (colour_depth2 == 16); 154 155 /* set the outputs */ 156 switch (si->ps.card_type) 157 { 158 case G400: 159 case G400MAX: 160 /* setup vertical timing adjust for crtc connected to the MAVEN: 161 * assuming connected straight through. */ 162 /* (extra "blanking" line for MAVEN hardware design fault) */ 163 target2.timing.v_display++; 164 165 switch (target.flags & DUALHEAD_BITS) 166 { 167 case DUALHEAD_ON: 168 case DUALHEAD_CLONE: 169 gx00_general_dac_select(DS_CRTC1DAC_CRTC2MAVEN); 170 si->switched_crtcs = false; 171 break; 172 case DUALHEAD_SWITCH: 173 if (i2c_sec_tv_adapter() == B_OK) 174 { 175 /* Don't switch CRTC's because MAVEN YUV is impossible then, 176 * and primary head output will be limited to 135Mhz pixelclock. */ 177 LOG(4,("SETMODE: secondary TV-adapter detected, switching buffers\n")); 178 gx00_general_dac_select(DS_CRTC1DAC_CRTC2MAVEN); 179 si->switched_crtcs = true; 180 } 181 else 182 { 183 /* This limits the pixelclocks on both heads to 135Mhz, 184 * but you can use overlay on the other output now. */ 185 LOG(4,("SETMODE: no secondary TV-adapter detected, switching CRTCs\n")); 186 gx00_general_dac_select(DS_CRTC1MAVEN_CRTC2DAC); 187 si->switched_crtcs = false; 188 /* re-calculate MAVEN-CRTC delay: formula valid for crossed CRTC's */ 189 si->crtc_delay = 17 + 4 * (colour_depth1 == 16); 190 /* re-setup vertical timing adjust for crtc connected to the MAVEN: 191 * cross connected. */ 192 /* (extra "blanking" line for MAVEN hardware design fault) */ 193 target.timing.v_display++; 194 target2.timing.v_display--; 195 } 196 break; 197 } 198 break; 199 case G450: 200 case G550: 201 if (!si->ps.primary_dvi) 202 /* output connector use is always 'straight-through' */ 203 //fixme: re-evaluate when DVI is setup... 204 { 205 switch (target.flags & DUALHEAD_BITS) 206 { 207 case DUALHEAD_ON: 208 case DUALHEAD_CLONE: 209 gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2); 210 si->switched_crtcs = false; 211 break; 212 case DUALHEAD_SWITCH: 213 if (i2c_sec_tv_adapter() == B_OK) 214 { 215 /* Don't switch CRTC's because MAVEN YUV and TVout is impossible then, 216 * and primary head output will be limited to 235Mhz pixelclock. */ 217 LOG(4,("SETMODE: secondary TV-adapter detected, switching buffers\n")); 218 gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2); 219 si->switched_crtcs = true; 220 } 221 else 222 { 223 /* This limits the pixelclocks on both heads to 235Mhz, 224 * but you can use overlay on the other output now. */ 225 LOG(4,("SETMODE: no secondary TV-adapter detected, switching CRTCs\n")); 226 gx00_general_dac_select(DS_CRTC1CON2_CRTC2CON1); 227 si->switched_crtcs = false; 228 } 229 break; 230 } 231 } 232 else 233 /* output connector use is cross-linked if no TV cable connected! */ 234 //fixme: re-evaluate when DVI is setup... 235 { 236 switch (target.flags & DUALHEAD_BITS) 237 { 238 case DUALHEAD_ON: 239 case DUALHEAD_CLONE: 240 if (i2c_sec_tv_adapter() == B_OK) 241 { 242 gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2); 243 si->switched_crtcs = false; 244 } 245 else 246 { 247 /* This limits the pixelclocks on both heads to 235Mhz, 248 * but you can use overlay on the other output now. */ 249 gx00_general_dac_select(DS_CRTC1CON2_CRTC2CON1); 250 si->switched_crtcs = false; 251 } 252 break; 253 case DUALHEAD_SWITCH: 254 if (i2c_sec_tv_adapter() == B_OK) 255 { 256 /* Don't switch CRTC's because MAVEN YUV and TVout is impossible then, 257 * and primary head output will be limited to 235Mhz pixelclock. */ 258 LOG(4,("SETMODE: secondary TV-adapter detected, switching buffers\n")); 259 gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2); 260 si->switched_crtcs = true; 261 } 262 else 263 { 264 LOG(4,("SETMODE: no secondary TV-adapter detected, switching CRTCs\n")); 265 gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2); 266 si->switched_crtcs = false; 267 } 268 break; 269 } 270 } 271 break; 272 default: 273 break; 274 } 275 276 if (si->switched_crtcs) 277 { 278 uint32 temp = startadd; 279 startadd = startadd_right; 280 startadd_right = temp; 281 } 282 283 /*Tell card what memory to display*/ 284 switch (target.flags & DUALHEAD_BITS) 285 { 286 case DUALHEAD_ON: 287 case DUALHEAD_SWITCH: 288 gx00_crtc_set_display_start(startadd,colour_depth1); 289 g400_crtc2_set_display_start(startadd_right,colour_depth2); 290 break; 291 case DUALHEAD_CLONE: 292 gx00_crtc_set_display_start(startadd,colour_depth1); 293 g400_crtc2_set_display_start(startadd,colour_depth2); 294 break; 295 } 296 297 /* set the timing */ 298 gx00_crtc_set_timing(target); 299 /* we do not need to setup CRTC2 here for a head that's in TVout mode */ 300 if (!(target2.flags & TV_BITS)) result = g400_crtc2_set_timing(target2); 301 302 /* TVout support: setup CRTC2 and it's pixelclock */ 303 if (si->ps.secondary_tvout && (target2.flags & TV_BITS)) maventv_init(target2); 304 } 305 else /* single head mode */ 306 { 307 status_t status; 308 int colour_mode = BPP32; 309 310 switch(target.space) 311 { 312 case B_CMAP8: colour_depth1 = 8; colour_mode = BPP8; break; 313 case B_RGB15_LITTLE: colour_depth1 = 16; colour_mode = BPP15; break; 314 case B_RGB16_LITTLE: colour_depth1 = 16; colour_mode = BPP16; break; 315 case B_RGB32_LITTLE: colour_depth1 = 32; colour_mode = BPP32; break; 316 default: 317 LOG(8,("SETMODE: Invalid singlehead colour depth 0x%08x\n", target.space)); 318 return B_ERROR; 319 } 320 321 /* set the pixel clock PLL */ 322 if (si->ps.card_type >= G100) 323 status = gx00_dac_set_pix_pll(target); 324 else 325 { 326 status = mil2_dac_set_pix_pll((target.timing.pixel_clock)/1000.0, colour_depth1); 327 } 328 if (status==B_ERROR) 329 LOG(8,("CRTC: error setting pixel clock (internal DAC)\n")); 330 331 /* set the colour depth for CRTC1 and the DAC */ 332 gx00_dac_mode(colour_mode,1.0); 333 gx00_crtc_depth(colour_mode); 334 335 /* set the display pitch */ 336 gx00_crtc_set_display_pitch(); 337 338 /* tell the card what memory to display */ 339 gx00_crtc_set_display_start(startadd,colour_depth1); 340 341 /* enable primary analog output */ 342 switch (si->ps.card_type) 343 { 344 case G100: 345 case G200: 346 case G400: 347 case G400MAX: 348 gx00_general_dac_select(DS_CRTC1DAC_CRTC2MAVEN); 349 break; 350 case G450: 351 case G550: 352 gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2); 353 gx50_general_output_select(); 354 break; 355 default: 356 break; 357 } 358 359 /* set the timing */ 360 gx00_crtc_set_timing(target); 361 362 //fixme: shut-off the videoPLL if it exists... 363 } 364 365 /* update driver's mode store */ 366 si->dm = target; 367 368 /* set up acceleration for this mode */ 369 gx00_acc_init(); 370 371 /* restore screen(s) output state(s) */ 372 SET_DPMS_MODE(si->dpms_flags); 373 374 /* clear line at bottom of screen if dualhead mode: 375 * MAVEN hardware design fault 'fix'. */ 376 if ((target.flags & DUALHEAD_BITS) && (si->ps.card_type <= G400MAX)) 377 gx00_maven_clrline(); 378 379 LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0)); 380 381 /* enable interrupts using the kernel driver */ 382 interrupt_enable(true); 383 384 /* optimize memory-access if needed */ 385 gx00_crtc_mem_priority(colour_depth1); 386 387 /* Tune RAM CAS-latency if needed. Must be done *here*! */ 388 mga_set_cas_latency(); 389 390 return B_OK; 391 } 392 393 /* 394 Set which pixel of the virtual frame buffer will show up in the 395 top left corner of the display device. Used for page-flipping 396 games and virtual desktops. 397 */ 398 status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) { 399 uint8 colour_depth; 400 uint32 startadd,startadd_right; 401 402 LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start)); 403 404 /* G400 CRTC1 handles multiples of 8 for 8bit, 4 for 16bit, 2 for 32 bit 405 G400 CRTC2 handles multiples of 32 for 16-bit and 16 for 32-bit - must stoop to this in dualhead 406 */ 407 408 /* reset lower bits, don't return an error! */ 409 if (si->dm.flags & DUALHEAD_BITS) 410 { 411 switch(si->dm.space) 412 { 413 case B_RGB16_LITTLE: 414 colour_depth=16; 415 h_display_start &= ~0x1f; 416 break; 417 case B_RGB32_LITTLE: 418 colour_depth=32; 419 h_display_start &= ~0x0f; 420 break; 421 default: 422 LOG(8,("SET:Invalid DH colour depth 0x%08x, should never happen\n", si->dm.space)); 423 return B_ERROR; 424 } 425 } 426 else 427 { 428 switch(si->dm.space) 429 { 430 case B_CMAP8: 431 colour_depth=8; 432 h_display_start &= ~0x07; 433 break; 434 case B_RGB15_LITTLE: case B_RGB16_LITTLE: 435 colour_depth=16; 436 h_display_start &= ~0x03; 437 break; 438 case B_RGB32_LITTLE: 439 colour_depth=32; 440 h_display_start &= ~0x01; 441 break; 442 default: 443 return B_ERROR; 444 } 445 } 446 447 /* do not run past end of display */ 448 switch (si->dm.flags & DUALHEAD_BITS) 449 { 450 case DUALHEAD_ON: 451 case DUALHEAD_SWITCH: 452 if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width) 453 return B_ERROR; 454 break; 455 default: 456 if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width) 457 return B_ERROR; 458 break; 459 } 460 if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height) 461 return B_ERROR; 462 463 /* everybody remember where we parked... */ 464 si->dm.h_display_start = h_display_start; 465 si->dm.v_display_start = v_display_start; 466 467 /* actually set the registers */ 468 //fixme: seperate both heads: we need a secondary si->fbc! 469 startadd = v_display_start * si->fbc.bytes_per_row; 470 startadd += h_display_start * (colour_depth >> 3); 471 startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer; 472 startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3); 473 474 /* account for switched CRTC's */ 475 if (si->switched_crtcs) 476 { 477 uint32 temp = startadd; 478 startadd = startadd_right; 479 startadd_right = temp; 480 } 481 482 interrupt_enable(false); 483 484 switch (si->dm.flags&DUALHEAD_BITS) 485 { 486 case DUALHEAD_ON: 487 case DUALHEAD_SWITCH: 488 gx00_crtc_set_display_start(startadd,colour_depth); 489 g400_crtc2_set_display_start(startadd_right,colour_depth); 490 break; 491 case DUALHEAD_OFF: 492 gx00_crtc_set_display_start(startadd,colour_depth); 493 break; 494 case DUALHEAD_CLONE: 495 gx00_crtc_set_display_start(startadd,colour_depth); 496 g400_crtc2_set_display_start(startadd,colour_depth); 497 break; 498 } 499 500 interrupt_enable(true); 501 return B_OK; 502 } 503 504 /* 505 Set the indexed color palette. 506 */ 507 void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) { 508 int i; 509 uint8 *r,*g,*b; 510 511 /* Protect gamma correction when not in CMAP8 */ 512 if (si->dm.space != B_CMAP8) return; 513 514 r=si->color_data; 515 g=r+256; 516 b=g+256; 517 518 i=first; 519 while (count--) 520 { 521 r[i]=*color_data++; 522 g[i]=*color_data++; 523 b[i]=*color_data++; 524 i++; 525 } 526 gx00_dac_palette(r,g,b); 527 } 528 529 /* Put the display into one of the Display Power Management modes. */ 530 status_t SET_DPMS_MODE(uint32 dpms_flags) 531 { 532 interrupt_enable(false); 533 534 LOG(4,("SET_DPMS_MODE: 0x%08x\n", dpms_flags)); 535 536 /* note current DPMS state for our reference */ 537 si->dpms_flags = dpms_flags; 538 539 if (si->dm.flags & DUALHEAD_BITS) /* dualhead */ 540 { 541 switch(dpms_flags) 542 { 543 case B_DPMS_ON: /* H: on, V: on */ 544 gx00_crtc_dpms(true, true, true); 545 if (si->ps.secondary_head) g400_crtc2_dpms(true, true, true); 546 break; 547 case B_DPMS_STAND_BY: 548 if (si->settings.greensync) 549 { 550 /* blank screen, but keep sync running */ 551 gx00_crtc_dpms(false, true, true); 552 } 553 else 554 { 555 gx00_crtc_dpms(false, false, true); 556 } 557 if (si->ps.secondary_head) 558 { 559 if ((si->dm.flags & TV_BITS) && (si->ps.card_type > G400MAX)) 560 { 561 /* keep display enabled in TVout modes for G450 and G550! */ 562 g400_crtc2_dpms(true, false, true); 563 } 564 else 565 { 566 g400_crtc2_dpms(false, false, true); 567 } 568 } 569 break; 570 case B_DPMS_SUSPEND: 571 if (si->settings.greensync) 572 { 573 /* blank screen, but keep sync running */ 574 gx00_crtc_dpms(false, true, true); 575 } 576 else 577 { 578 gx00_crtc_dpms(false, true, false); 579 } 580 if (si->ps.secondary_head) 581 { 582 if ((si->dm.flags & TV_BITS) && (si->ps.card_type > G400MAX)) 583 { 584 /* keep display enabled in TVout modes for G450 and G550! */ 585 g400_crtc2_dpms(true, true, false); 586 } 587 else 588 { 589 g400_crtc2_dpms(false, true, false); 590 } 591 } 592 break; 593 case B_DPMS_OFF: /* H: off, V: off, display off */ 594 if (si->settings.greensync) 595 { 596 /* blank screen, but keep sync running */ 597 gx00_crtc_dpms(false, true, true); 598 } 599 else 600 { 601 gx00_crtc_dpms(false, false, false); 602 } 603 if (si->ps.secondary_head) 604 { 605 if ((si->dm.flags & TV_BITS) && (si->ps.card_type > G400MAX)) 606 { 607 /* keep display enabled in TVout modes for G450 and G550! */ 608 g400_crtc2_dpms(true, false, false); 609 } 610 else 611 { 612 g400_crtc2_dpms(false, false, false); 613 } 614 } 615 break; 616 default: 617 LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags)); 618 interrupt_enable(true); 619 return B_ERROR; 620 } 621 } 622 else /* singlehead */ 623 { 624 switch(dpms_flags) 625 { 626 case B_DPMS_ON: /* H: on, V: on */ 627 gx00_crtc_dpms(true, true, true); 628 break; 629 case B_DPMS_STAND_BY: 630 if (si->settings.greensync) 631 { 632 /* blank screen, but keep sync running */ 633 gx00_crtc_dpms(false, true, true); 634 } 635 else 636 { 637 gx00_crtc_dpms(false, false, true); 638 } 639 break; 640 case B_DPMS_SUSPEND: 641 if (si->settings.greensync) 642 { 643 /* blank screen, but keep sync running */ 644 gx00_crtc_dpms(false, true, true); 645 } 646 else 647 { 648 gx00_crtc_dpms(false, true, false); 649 } 650 break; 651 case B_DPMS_OFF: /* H: off, V: off, display off */ 652 if (si->settings.greensync) 653 { 654 /* blank screen, but keep sync running */ 655 gx00_crtc_dpms(false, true, true); 656 } 657 else 658 { 659 gx00_crtc_dpms(false, false, false); 660 } 661 break; 662 default: 663 LOG(8,("SET: Invalid DPMS settings (SH) 0x%08x\n", dpms_flags)); 664 interrupt_enable(true); 665 return B_ERROR; 666 } 667 } 668 interrupt_enable(true); 669 return B_OK; 670 } 671 672 /* Report device DPMS capabilities. */ 673 uint32 DPMS_CAPABILITIES(void) 674 { 675 if (si->settings.greensync) 676 /* we can blank the screen on CRTC1, G400 CRTC2 does not support intermediate 677 * modes anyway. */ 678 //fixme: G450/G550 support full DPMS on CRTC2... 679 return B_DPMS_ON | B_DPMS_OFF; 680 else 681 /* normally CRTC1 supports full DPMS (and on G450/G550 CRTC2 also).. */ 682 return B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF; 683 } 684 685 /* Return the current DPMS mode. */ 686 uint32 DPMS_MODE(void) 687 { 688 return si->dpms_flags; 689 } 690