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