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