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/2015 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 %ukHz\n", target.timing.pixel_clock)); 45 LOG(1, ("SETMODE: requested virtual_width %u, virtual_height %u\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.block_acc) { 296 if (!si->settings.dma_acc) 297 nv_acc_init(); 298 else 299 nv_acc_init_dma(); 300 } 301 302 /* set up overlay unit for this mode */ 303 nv_bes_init(); 304 305 /* note freemem range */ 306 /* first free adress follows hardcursor and workspace */ 307 si->engine.threeD.mem_low = si->fbc.bytes_per_row * si->dm.virtual_height; 308 if (si->settings.hardcursor) si->engine.threeD.mem_low += 2048; 309 /* last free adress is end-of-ram minus max space needed for overlay bitmaps */ 310 //fixme possible: 311 //if overlay buffers are allocated subtract buffersize from mem_high; 312 //only allocate overlay buffers if 3D is not in use. (block overlay during 3D) 313 si->engine.threeD.mem_high = si->ps.memory_size - 1; 314 /* Keep some extra distance as a workaround for certain bugs (see 315 * DriverInterface.h for an explanation). */ 316 if (si->ps.card_arch < NV40A) 317 si->engine.threeD.mem_high -= PRE_NV40_OFFSET; 318 else 319 si->engine.threeD.mem_high -= NV40_PLUS_OFFSET; 320 321 si->engine.threeD.mem_high -= (MAXBUFFERS * 1024 * 1024 * 2); /* see overlay.c file */ 322 323 /* restore screen(s) output state(s) */ 324 SET_DPMS_MODE(si->dpms_flags); 325 326 /* enable interrupts using the kernel driver */ 327 //fixme: 328 //add head2 once we use one driver instance 'per head' (instead of 'per card') 329 head1_interrupt_enable(true); 330 331 /* make sure a possible 3D add-on will re-initialize itself by signalling ready */ 332 si->engine.threeD.mode_changing = false; 333 334 /* optimize memory-access if needed */ 335 // head1_mem_priority(colour_depth1); 336 337 /* Tune RAM CAS-latency if needed. Must be done *here*! */ 338 nv_set_cas_latency(); 339 340 LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0)); 341 342 return B_OK; 343 } 344 345 /* 346 Set which pixel of the virtual frame buffer will show up in the 347 top left corner of the display device. Used for page-flipping 348 games and virtual desktops. 349 */ 350 status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) { 351 uint8 colour_depth; 352 uint32 startadd,startadd_right; 353 354 LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start)); 355 356 /* nVidia cards support pixelprecise panning on both heads in all modes: 357 * No stepping granularity needed! */ 358 359 /* determine bits used for the colordepth */ 360 switch(si->dm.space) 361 { 362 case B_CMAP8: 363 colour_depth=8; 364 break; 365 case B_RGB15_LITTLE: 366 case B_RGB16_LITTLE: 367 colour_depth=16; 368 break; 369 case B_RGB24_LITTLE: 370 colour_depth=24; 371 break; 372 case B_RGB32_LITTLE: 373 colour_depth=32; 374 break; 375 default: 376 return B_ERROR; 377 } 378 379 /* do not run past end of display */ 380 switch (si->dm.flags & DUALHEAD_BITS) 381 { 382 case DUALHEAD_ON: 383 case DUALHEAD_SWITCH: 384 if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width) 385 return B_ERROR; 386 break; 387 default: 388 if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width) 389 return B_ERROR; 390 break; 391 } 392 if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height) 393 return B_ERROR; 394 395 /* everybody remember where we parked... */ 396 si->dm.h_display_start = h_display_start; 397 si->dm.v_display_start = v_display_start; 398 399 /* actually set the registers */ 400 //fixme: seperate both heads: we need a secondary si->fbc! 401 startadd = v_display_start * si->fbc.bytes_per_row; 402 startadd += h_display_start * (colour_depth >> 3); 403 startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer; 404 startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3); 405 406 /* disable interrupts using the kernel driver */ 407 head1_interrupt_enable(false); 408 if (si->ps.secondary_head) head2_interrupt_enable(false); 409 410 switch (si->dm.flags & DUALHEAD_BITS) 411 { 412 case DUALHEAD_ON: 413 case DUALHEAD_SWITCH: 414 head1_set_display_start(startadd,colour_depth); 415 head2_set_display_start(startadd_right,colour_depth); 416 break; 417 case DUALHEAD_OFF: 418 head1_set_display_start(startadd,colour_depth); 419 break; 420 case DUALHEAD_CLONE: 421 head1_set_display_start(startadd,colour_depth); 422 head2_set_display_start(startadd,colour_depth); 423 break; 424 } 425 426 //fixme: 427 //add head2 once we use one driver instance 'per head' (instead of 'per card') 428 head1_interrupt_enable(true); 429 430 return B_OK; 431 } 432 433 /* Set the indexed color palette */ 434 void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) { 435 int i; 436 uint8 *r,*g,*b; 437 438 /* Protect gamma correction when not in CMAP8 */ 439 if (si->dm.space != B_CMAP8) return; 440 441 r=si->color_data; 442 g=r+256; 443 b=g+256; 444 445 i=first; 446 while (count--) 447 { 448 r[i]=*color_data++; 449 g[i]=*color_data++; 450 b[i]=*color_data++; 451 i++; 452 } 453 head1_palette(r,g,b); 454 if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b); 455 } 456 457 /* Put the display into one of the Display Power Management modes. */ 458 status_t SET_DPMS_MODE(uint32 dpms_flags) 459 { 460 bool display, h1h, h1v, h2h, h2v, do_p1, do_p2; 461 462 /* disable interrupts using the kernel driver */ 463 head1_interrupt_enable(false); 464 if (si->ps.secondary_head) head2_interrupt_enable(false); 465 466 LOG(4,("SET_DPMS_MODE: $%08x\n", dpms_flags)); 467 468 /* note current DPMS state for our reference */ 469 si->dpms_flags = dpms_flags; 470 471 /* preset: DPMS for panels should be executed */ 472 do_p1 = do_p2 = true; 473 474 /* determine signals to send to head(s) */ 475 display = h1h = h1v = h2h = h2v = true; 476 switch(dpms_flags) 477 { 478 case B_DPMS_ON: /* H: on, V: on, display on */ 479 break; 480 case B_DPMS_STAND_BY: 481 display = h1h = h2h = false; 482 break; 483 case B_DPMS_SUSPEND: 484 display = h1v = h2v = false; 485 break; 486 case B_DPMS_OFF: /* H: off, V: off, display off */ 487 display = h1h = h1v = h2h = h2v = false; 488 break; 489 default: 490 LOG(8,("SET: Invalid DPMS settings $%08x\n", dpms_flags)); 491 //fixme: 492 //add head2 once we use one driver instance 'per head' (instead of 'per card') 493 head1_interrupt_enable(true); 494 495 return B_ERROR; 496 } 497 498 /* CRTC used for TVout needs specific DPMS programming */ 499 if (si->dm.flags & TV_BITS) 500 { 501 /* TV_PRIMARY tells us that the head to be used with TVout is the head that's 502 * actually assigned as being the primary head at powerup: 503 * so non dualhead-mode-dependant, and not 'fixed' CRTC1! */ 504 if (si->dm.flags & TV_PRIMARY) 505 { 506 LOG(4,("SET_DPMS_MODE: tuning primary head DPMS settings for TVout compatibility\n")); 507 508 if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) 509 { 510 if (!(si->settings.vga_on_tv)) 511 { 512 /* block VGA output on head displaying on TV */ 513 /* Note: 514 * this specific sync setting is required: Vsync is used to keep TVout 515 * synchronized to the CRTC 'vertically' (otherwise 'rolling' occurs). 516 * This leaves Hsync only for shutting off the VGA screen. */ 517 h1h = false; 518 h1v = true; 519 /* block panel DPMS updates */ 520 do_p1 = false; 521 } 522 else 523 { 524 /* when concurrent VGA is used alongside TVout on a head, DPMS is safest 525 * applied this way: Vsync is needed for stopping TVout successfully when 526 * a (new) modeswitch occurs. 527 * (see routine BT_stop_tvout() in nv_brooktreetv.c) */ 528 /* Note: 529 * applying 'normal' DPMS here and forcing Vsync on in the above mentioned 530 * routine seems to not always be enough: sometimes image generation will 531 * not resume in that case. */ 532 h1h = display; 533 h1v = true; 534 } 535 } 536 else 537 { 538 if (!(si->settings.vga_on_tv)) 539 { 540 h2h = false; 541 h2v = true; 542 do_p2 = false; 543 } 544 else 545 { 546 h2h = display; 547 h2v = true; 548 } 549 } 550 } 551 else 552 { 553 LOG(4,("SET_DPMS_MODE: tuning secondary head DPMS settings for TVout compatibility\n")); 554 555 if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) 556 { 557 if (!(si->settings.vga_on_tv)) 558 { 559 h2h = false; 560 h2v = true; 561 do_p2 = false; 562 } 563 else 564 { 565 h2h = display; 566 h2v = true; 567 } 568 } 569 else 570 { 571 if (!(si->settings.vga_on_tv)) 572 { 573 h1h = false; 574 h1v = true; 575 do_p1 = false; 576 } 577 else 578 { 579 h1h = display; 580 h1v = true; 581 } 582 } 583 } 584 } 585 586 /* issue actual DPMS commands as far as applicable */ 587 head1_dpms(display, h1h, h1v, do_p1); 588 if ((si->ps.secondary_head) && (si->dm.flags & DUALHEAD_BITS)) 589 head2_dpms(display, h2h, h2v, do_p2); 590 if (si->dm.flags & TV_BITS) 591 BT_dpms(display); 592 593 //fixme: 594 //add head2 once we use one driver instance 'per head' (instead of 'per card') 595 head1_interrupt_enable(true); 596 597 return B_OK; 598 } 599 600 /* Report device DPMS capabilities */ 601 uint32 DPMS_CAPABILITIES(void) 602 { 603 return (B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF); 604 } 605 606 /* Return the current DPMS mode */ 607 uint32 DPMS_MODE(void) 608 { 609 return si->dpms_flags; 610 } 611