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-2/2006 10 */ 11 12 #define MODULE_BIT 0x00200000 13 14 #include "acc_std.h" 15 16 /* First validate the mode, then call lots of bit banging stuff to set the mode(s)! */ 17 status_t SET_DISPLAY_MODE(display_mode *mode_to_set) 18 { 19 /* BOUNDS WARNING: 20 * It's impossible to deviate whatever small amount in a display_mode if the lower 21 * and upper limits are the same! 22 * Besides: 23 * BeOS (tested R5.0.3PE) is failing BWindowScreen::SetFrameBuffer() if PROPOSEMODE 24 * returns B_BAD_VALUE! 25 * Which means PROPOSEMODE should not return that on anything except on 26 * deviations for: 27 * display_mode.virtual_width; 28 * display_mode.virtual_height; 29 * display_mode.timing.h_display; 30 * display_mode.timing.v_display; 31 * So: 32 * We don't use bounds here by making sure bounds and target are the same struct! 33 * (See the call to PROPOSE_DISPLAY_MODE below) */ 34 display_mode /*bounds,*/ target; 35 36 uint8 colour_depth1 = 32; 37 uint32 startadd,startadd_right; 38 // bool crt1, crt2, cross; 39 40 /* Adjust mode to valid one and fail if invalid */ 41 target /*= bounds*/ = *mode_to_set; 42 /* show the mode bits */ 43 LOG(1, ("SETMODE: (ENTER) initial modeflags: $%08x\n", target.flags)); 44 LOG(1, ("SETMODE: requested target pixelclock %dkHz\n", target.timing.pixel_clock)); 45 LOG(1, ("SETMODE: requested virtual_width %d, virtual_height %d\n", 46 target.virtual_width, target.virtual_height)); 47 48 /* See BOUNDS WARNING above... */ 49 if (PROPOSE_DISPLAY_MODE(&target, &target, &target) == B_ERROR) return B_ERROR; 50 51 /* make sure a possible 3D add-on will block rendering and re-initialize itself. 52 * note: update in _this_ order only */ 53 /* SET_DISPLAY_MODE will reset this flag when it's done. */ 54 si->engine.threeD.mode_changing = true; 55 /* every 3D add-on will reset this bit-flag when it's done. */ 56 si->engine.threeD.newmode = 0xffffffff; 57 /* every 3D clone needs to reclaim a slot. 58 * note: this also cleans up reserved channels for killed 3D clones.. */ 59 si->engine.threeD.clones = 0x00000000; 60 61 /* disable interrupts using the kernel driver */ 62 head1_interrupt_enable(false); 63 if (si->ps.secondary_head) head2_interrupt_enable(false); 64 65 /* disable TVout if supported */ 66 if (si->ps.tvout) BT_stop_tvout(); 67 68 /* turn off screen(s) _after_ TVout is disabled (if applicable) */ 69 head1_dpms(false, false, false, true); 70 if (si->ps.secondary_head) head2_dpms(false, false, false, true); 71 if (si->ps.tvout) BT_dpms(false); 72 73 /*where in framebuffer the screen is (should this be dependant on previous MOVEDISPLAY?)*/ 74 startadd = (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer; 75 76 /* calculate and set new mode bytes_per_row */ 77 nv_general_validate_pic_size (&target, &si->fbc.bytes_per_row, &si->acc_mode); 78 79 /*Perform the very long mode switch!*/ 80 if (target.flags & DUALHEAD_BITS) /*if some dualhead mode*/ 81 { 82 uint8 colour_depth2 = colour_depth1; 83 84 /* init display mode for secondary head */ 85 display_mode target2 = target; 86 87 LOG(1,("SETMODE: setting DUALHEAD mode\n")); 88 89 /* validate flags for secondary TVout */ 90 //fixme: remove or block on autodetect fail. (is now shutoff) 91 if ((0) && (target2.flags & TV_BITS)) 92 { 93 target.flags &= ~TV_BITS;//still needed for some routines... 94 target2.flags &= ~TV_BITS; 95 LOG(1,("SETMODE: blocking TVout: no TVout cable connected!\n")); 96 } 97 98 /* detect which connectors have a CRT connected */ 99 //fixme: 'hot-plugging' for analog monitors removed: remove code as well; 100 //or make it work with digital panels connected as well. 101 // crt1 = nv_dac_crt_connected(); 102 // crt2 = nv_dac2_crt_connected(); 103 /* connect outputs 'straight-through' */ 104 // if (crt1) 105 // { 106 /* connector1 is used as primary output */ 107 // cross = false; 108 // } 109 // else 110 // { 111 // if (crt2) 112 /* connector2 is used as primary output */ 113 // cross = true; 114 // else 115 /* no CRT detected: assume connector1 is used as primary output */ 116 // cross = false; 117 // } 118 /* set output connectors assignment if possible */ 119 if ((target.flags & DUALHEAD_BITS) == DUALHEAD_SWITCH) 120 /* invert output assignment in switch mode */ 121 nv_general_head_select(true); 122 else 123 nv_general_head_select(false); 124 125 /* set the pixel clock PLL(s) */ 126 LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock)); 127 if (head1_set_pix_pll(target) == B_ERROR) 128 LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n")); 129 130 LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock)); 131 if (head2_set_pix_pll(target2) == B_ERROR) 132 LOG(8,("SETMODE: error setting pixel clock (DAC2)\n")); 133 134 /*set the colour depth for CRTC1 and the DAC */ 135 switch(target.space) 136 { 137 case B_CMAP8: 138 colour_depth1 = 8; 139 head1_mode(BPP8, 1.0); 140 head1_depth(BPP8); 141 break; 142 case B_RGB15_LITTLE: 143 colour_depth1 = 16; 144 head1_mode(BPP15, 1.0); 145 head1_depth(BPP15); 146 break; 147 case B_RGB16_LITTLE: 148 colour_depth1 = 16; 149 head1_mode(BPP16, 1.0); 150 head1_depth(BPP16); 151 break; 152 case B_RGB32_LITTLE: 153 colour_depth1 = 32; 154 head1_mode(BPP32, 1.0); 155 head1_depth(BPP32); 156 break; 157 } 158 /*set the colour depth for CRTC2 and DAC2 */ 159 switch(target2.space) 160 { 161 case B_CMAP8: 162 colour_depth2 = 8; 163 head2_mode(BPP8, 1.0); 164 head2_depth(BPP8); 165 break; 166 case B_RGB15_LITTLE: 167 colour_depth2 = 16; 168 head2_mode(BPP15, 1.0); 169 head2_depth(BPP15); 170 break; 171 case B_RGB16_LITTLE: 172 colour_depth2 = 16; 173 head2_mode(BPP16, 1.0); 174 head2_depth(BPP16); 175 break; 176 case B_RGB32_LITTLE: 177 colour_depth2 = 32; 178 head2_mode(BPP32, 1.0); 179 head2_depth(BPP32); 180 break; 181 } 182 183 /* check if we are doing interlaced TVout mode */ 184 //fixme: we don't support interlaced mode? 185 si->interlaced_tv_mode = false; 186 187 /*set the display(s) pitches*/ 188 head1_set_display_pitch (); 189 //fixme: seperate for real dualhead modes: 190 //we need a secondary si->fbc! 191 head2_set_display_pitch (); 192 193 /*work out where the "right" screen starts*/ 194 startadd_right = startadd + (target.timing.h_display * (colour_depth1 >> 3)); 195 196 /* Tell card what memory to display */ 197 switch (target.flags & DUALHEAD_BITS) 198 { 199 case DUALHEAD_ON: 200 case DUALHEAD_SWITCH: 201 head1_set_display_start(startadd,colour_depth1); 202 head2_set_display_start(startadd_right,colour_depth2); 203 break; 204 case DUALHEAD_CLONE: 205 head1_set_display_start(startadd,colour_depth1); 206 head2_set_display_start(startadd,colour_depth2); 207 break; 208 } 209 210 /* set the timing */ 211 head1_set_timing(target); 212 head2_set_timing(target2); 213 214 /* TVout support: program TVout encoder and modify CRTC timing */ 215 if (si->ps.tvout && (target2.flags & TV_BITS)) BT_setmode(target2); 216 } 217 else /* single head mode */ 218 { 219 int colour_mode = BPP32; 220 221 /* connect output */ 222 if (si->ps.secondary_head) 223 { 224 /* detect which connectors have a CRT connected */ 225 //fixme: 'hot-plugging' for analog monitors removed: remove code as well; 226 //or make it work with digital panels connected as well. 227 // crt1 = nv_dac_crt_connected(); 228 // crt2 = nv_dac2_crt_connected(); 229 /* connect outputs 'straight-through' */ 230 // if (crt1) 231 // { 232 /* connector1 is used as primary output */ 233 // cross = false; 234 // } 235 // else 236 // { 237 // if (crt2) 238 /* connector2 is used as primary output */ 239 // cross = true; 240 // else 241 /* no CRT detected: assume connector1 is used as primary output */ 242 // cross = false; 243 // } 244 /* set output connectors assignment if possible */ 245 nv_general_head_select(false); 246 } 247 248 switch(target.space) 249 { 250 case B_CMAP8: colour_depth1 = 8; colour_mode = BPP8; break; 251 case B_RGB15_LITTLE: colour_depth1 = 16; colour_mode = BPP15; break; 252 case B_RGB16_LITTLE: colour_depth1 = 16; colour_mode = BPP16; break; 253 case B_RGB32_LITTLE: colour_depth1 = 32; colour_mode = BPP32; break; 254 default: 255 LOG(8,("SETMODE: Invalid singlehead colour depth 0x%08x\n", target.space)); 256 return B_ERROR; 257 } 258 259 /* set the pixel clock PLL */ 260 if (head1_set_pix_pll(target) == B_ERROR) 261 LOG(8,("CRTC: error setting pixel clock (internal DAC)\n")); 262 263 /* set the colour depth for CRTC1 and the DAC */ 264 /* first set the colordepth */ 265 head1_depth(colour_mode); 266 /* then(!) program the PAL (<8bit colordepth does not support 8bit PAL) */ 267 head1_mode(colour_mode,1.0); 268 269 /* set the display pitch */ 270 head1_set_display_pitch(); 271 272 /* tell the card what memory to display */ 273 head1_set_display_start(startadd,colour_depth1); 274 275 /* set the timing */ 276 head1_set_timing(target); 277 278 /* TVout support: program TVout encoder and modify CRTC timing */ 279 if (si->ps.tvout && (target.flags & TV_BITS)) BT_setmode(target); 280 281 //fixme: shut-off the videoPLL if it exists... 282 } 283 284 /* update driver's mode store */ 285 si->dm = target; 286 287 /* update FIFO data fetching according to mode */ 288 nv_crtc_update_fifo(); 289 if (si->ps.secondary_head) nv_crtc2_update_fifo(); 290 291 /* set up acceleration for this mode */ 292 /* note: 293 * Maybe later we can forget about non-DMA mode (depends on 3D acceleration 294 * attempts). */ 295 if (!si->settings.dma_acc) 296 nv_acc_init(); 297 else 298 nv_acc_init_dma(); 299 /* set up overlay unit for this mode */ 300 nv_bes_init(); 301 302 /* note freemem range */ 303 /* first free adress follows hardcursor and workspace */ 304 si->engine.threeD.mem_low = si->fbc.bytes_per_row * si->dm.virtual_height; 305 if (si->settings.hardcursor) si->engine.threeD.mem_low += 2048; 306 /* last free adress is end-of-ram minus max space needed for overlay bitmaps */ 307 //fixme possible: 308 //if overlay buffers are allocated subtract buffersize from mem_high; 309 //only allocate overlay buffers if 3D is not in use. (block overlay during 3D) 310 si->engine.threeD.mem_high = si->ps.memory_size - 1; 311 /* Keep some extra distance as a workaround for certain bugs (see 312 * DriverInterface.h for an explanation). */ 313 if (si->ps.card_arch < NV40A) 314 si->engine.threeD.mem_high -= PRE_NV40_OFFSET; 315 else 316 si->engine.threeD.mem_high -= NV40_PLUS_OFFSET; 317 318 si->engine.threeD.mem_high -= (MAXBUFFERS * 1024 * 1024 * 2); /* see overlay.c file */ 319 320 /* restore screen(s) output state(s) */ 321 SET_DPMS_MODE(si->dpms_flags); 322 323 /* enable interrupts using the kernel driver */ 324 //fixme: 325 //add head2 once we use one driver instance 'per head' (instead of 'per card') 326 head1_interrupt_enable(true); 327 328 /* make sure a possible 3D add-on will re-initialize itself by signalling ready */ 329 si->engine.threeD.mode_changing = false; 330 331 /* optimize memory-access if needed */ 332 // head1_mem_priority(colour_depth1); 333 334 /* Tune RAM CAS-latency if needed. Must be done *here*! */ 335 nv_set_cas_latency(); 336 337 LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0)); 338 339 return B_OK; 340 } 341 342 /* 343 Set which pixel of the virtual frame buffer will show up in the 344 top left corner of the display device. Used for page-flipping 345 games and virtual desktops. 346 */ 347 status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) { 348 uint8 colour_depth; 349 uint32 startadd,startadd_right; 350 351 LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start)); 352 353 /* nVidia cards support pixelprecise panning on both heads in all modes: 354 * No stepping granularity needed! */ 355 356 /* determine bits used for the colordepth */ 357 switch(si->dm.space) 358 { 359 case B_CMAP8: 360 colour_depth=8; 361 break; 362 case B_RGB15_LITTLE: 363 case B_RGB16_LITTLE: 364 colour_depth=16; 365 break; 366 case B_RGB24_LITTLE: 367 colour_depth=24; 368 break; 369 case B_RGB32_LITTLE: 370 colour_depth=32; 371 break; 372 default: 373 return B_ERROR; 374 } 375 376 /* do not run past end of display */ 377 switch (si->dm.flags & DUALHEAD_BITS) 378 { 379 case DUALHEAD_ON: 380 case DUALHEAD_SWITCH: 381 if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width) 382 return B_ERROR; 383 break; 384 default: 385 if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width) 386 return B_ERROR; 387 break; 388 } 389 if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height) 390 return B_ERROR; 391 392 /* everybody remember where we parked... */ 393 si->dm.h_display_start = h_display_start; 394 si->dm.v_display_start = v_display_start; 395 396 /* actually set the registers */ 397 //fixme: seperate both heads: we need a secondary si->fbc! 398 startadd = v_display_start * si->fbc.bytes_per_row; 399 startadd += h_display_start * (colour_depth >> 3); 400 startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer; 401 startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3); 402 403 /* disable interrupts using the kernel driver */ 404 head1_interrupt_enable(false); 405 if (si->ps.secondary_head) head2_interrupt_enable(false); 406 407 switch (si->dm.flags & DUALHEAD_BITS) 408 { 409 case DUALHEAD_ON: 410 case DUALHEAD_SWITCH: 411 head1_set_display_start(startadd,colour_depth); 412 head2_set_display_start(startadd_right,colour_depth); 413 break; 414 case DUALHEAD_OFF: 415 head1_set_display_start(startadd,colour_depth); 416 break; 417 case DUALHEAD_CLONE: 418 head1_set_display_start(startadd,colour_depth); 419 head2_set_display_start(startadd,colour_depth); 420 break; 421 } 422 423 //fixme: 424 //add head2 once we use one driver instance 'per head' (instead of 'per card') 425 head1_interrupt_enable(true); 426 427 return B_OK; 428 } 429 430 /* Set the indexed color palette */ 431 void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) { 432 int i; 433 uint8 *r,*g,*b; 434 435 /* Protect gamma correction when not in CMAP8 */ 436 if (si->dm.space != B_CMAP8) return; 437 438 r=si->color_data; 439 g=r+256; 440 b=g+256; 441 442 i=first; 443 while (count--) 444 { 445 r[i]=*color_data++; 446 g[i]=*color_data++; 447 b[i]=*color_data++; 448 i++; 449 } 450 head1_palette(r,g,b); 451 if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b); 452 } 453 454 /* Put the display into one of the Display Power Management modes. */ 455 status_t SET_DPMS_MODE(uint32 dpms_flags) 456 { 457 bool display, h1h, h1v, h2h, h2v, do_p1, do_p2; 458 459 /* disable interrupts using the kernel driver */ 460 head1_interrupt_enable(false); 461 if (si->ps.secondary_head) head2_interrupt_enable(false); 462 463 LOG(4,("SET_DPMS_MODE: $%08x\n", dpms_flags)); 464 465 /* note current DPMS state for our reference */ 466 si->dpms_flags = dpms_flags; 467 468 /* preset: DPMS for panels should be executed */ 469 do_p1 = do_p2 = true; 470 471 /* determine signals to send to head(s) */ 472 display = h1h = h1v = h2h = h2v = true; 473 switch(dpms_flags) 474 { 475 case B_DPMS_ON: /* H: on, V: on, display on */ 476 break; 477 case B_DPMS_STAND_BY: 478 display = h1h = h2h = false; 479 break; 480 case B_DPMS_SUSPEND: 481 display = h1v = h2v = false; 482 break; 483 case B_DPMS_OFF: /* H: off, V: off, display off */ 484 display = h1h = h1v = h2h = h2v = false; 485 break; 486 default: 487 LOG(8,("SET: Invalid DPMS settings $%08x\n", dpms_flags)); 488 //fixme: 489 //add head2 once we use one driver instance 'per head' (instead of 'per card') 490 head1_interrupt_enable(true); 491 492 return B_ERROR; 493 } 494 495 /* CRTC used for TVout needs specific DPMS programming */ 496 if (si->dm.flags & TV_BITS) 497 { 498 /* TV_PRIMARY tells us that the head to be used with TVout is the head that's 499 * actually assigned as being the primary head at powerup: 500 * so non dualhead-mode-dependant, and not 'fixed' CRTC1! */ 501 if (si->dm.flags & TV_PRIMARY) 502 { 503 LOG(4,("SET_DPMS_MODE: tuning primary head DPMS settings for TVout compatibility\n")); 504 505 if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) 506 { 507 if (!(si->settings.vga_on_tv)) 508 { 509 /* block VGA output on head displaying on TV */ 510 /* Note: 511 * this specific sync setting is required: Vsync is used to keep TVout 512 * synchronized to the CRTC 'vertically' (otherwise 'rolling' occurs). 513 * This leaves Hsync only for shutting off the VGA screen. */ 514 h1h = false; 515 h1v = true; 516 /* block panel DPMS updates */ 517 do_p1 = false; 518 } 519 else 520 { 521 /* when concurrent VGA is used alongside TVout on a head, DPMS is safest 522 * applied this way: Vsync is needed for stopping TVout successfully when 523 * a (new) modeswitch occurs. 524 * (see routine BT_stop_tvout() in nv_brooktreetv.c) */ 525 /* Note: 526 * applying 'normal' DPMS here and forcing Vsync on in the above mentioned 527 * routine seems to not always be enough: sometimes image generation will 528 * not resume in that case. */ 529 h1h = display; 530 h1v = true; 531 } 532 } 533 else 534 { 535 if (!(si->settings.vga_on_tv)) 536 { 537 h2h = false; 538 h2v = true; 539 do_p2 = false; 540 } 541 else 542 { 543 h2h = display; 544 h2v = true; 545 } 546 } 547 } 548 else 549 { 550 LOG(4,("SET_DPMS_MODE: tuning secondary head DPMS settings for TVout compatibility\n")); 551 552 if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) 553 { 554 if (!(si->settings.vga_on_tv)) 555 { 556 h2h = false; 557 h2v = true; 558 do_p2 = false; 559 } 560 else 561 { 562 h2h = display; 563 h2v = true; 564 } 565 } 566 else 567 { 568 if (!(si->settings.vga_on_tv)) 569 { 570 h1h = false; 571 h1v = true; 572 do_p1 = false; 573 } 574 else 575 { 576 h1h = display; 577 h1v = true; 578 } 579 } 580 } 581 } 582 583 /* issue actual DPMS commands as far as applicable */ 584 head1_dpms(display, h1h, h1v, do_p1); 585 if ((si->ps.secondary_head) && (si->dm.flags & DUALHEAD_BITS)) 586 head2_dpms(display, h2h, h2v, do_p2); 587 if (si->dm.flags & TV_BITS) 588 BT_dpms(display); 589 590 //fixme: 591 //add head2 once we use one driver instance 'per head' (instead of 'per card') 592 head1_interrupt_enable(true); 593 594 return B_OK; 595 } 596 597 /* Report device DPMS capabilities */ 598 uint32 DPMS_CAPABILITIES(void) 599 { 600 return (B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF); 601 } 602 603 /* Return the current DPMS mode */ 604 uint32 DPMS_MODE(void) 605 { 606 return si->dpms_flags; 607 } 608