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-6/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 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 bool display, h, v; 55 // bool crt1, crt2, cross; 56 57 /* Adjust mode to valid one and fail if invalid */ 58 target /*= bounds*/ = *mode_to_set; 59 /* show the mode bits */ 60 LOG(1, ("SETMODE: (ENTER) initial modeflags: $%08x\n", target.flags)); 61 LOG(1, ("SETMODE: requested target pixelclock %dkHz\n", target.timing.pixel_clock)); 62 LOG(1, ("SETMODE: requested virtual_width %d, virtual_height %d\n", 63 target.virtual_width, target.virtual_height)); 64 65 /* See BOUNDS WARNING above... */ 66 if (PROPOSE_DISPLAY_MODE(&target, &target, &target) == B_ERROR) return B_ERROR; 67 68 /* if not dualhead capable card clear dualhead flags */ 69 if (!(target.flags & DUALHEAD_CAPABLE)) 70 { 71 target.flags &= ~DUALHEAD_BITS; 72 } 73 /* if not TVout capable card clear TVout flags */ 74 if (!(target.flags & TV_CAPABLE)) 75 { 76 target.flags &= ~TV_BITS; 77 } 78 LOG(1, ("SETMODE: (CONT.) validated command modeflags: $%08x\n", target.flags)); 79 80 /* make sure a possible 3D add-on will block rendering and re-initialize itself. 81 * note: update in _this_ order only */ 82 /* SET_DISPLAY_MODE will reset this flag when it's done. */ 83 si->engine.threeD.mode_changing = true; 84 /* every 3D add-on will reset this bit-flag when it's done. */ 85 si->engine.threeD.newmode = 0xffffffff; 86 /* every 3D clone needs to reclaim a slot. 87 * note: this also cleans up reserved channels for killed 3D clones.. */ 88 si->engine.threeD.clones = 0x00000000; 89 90 /* disable interrupts using the kernel driver */ 91 interrupt_enable(false); 92 93 /* find current DPMS state, then turn off screen(s) */ 94 head1_dpms_fetch(&display, &h, &v); 95 head1_dpms(false, false, false); 96 if (si->ps.secondary_head) head2_dpms(false, false, false); 97 98 /*where in framebuffer the screen is (should this be dependant on previous MOVEDISPLAY?)*/ 99 startadd = (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer; 100 101 /* calculate and set new mode bytes_per_row */ 102 nv_general_validate_pic_size (&target, &si->fbc.bytes_per_row, &si->acc_mode); 103 104 /*Perform the very long mode switch!*/ 105 if (target.flags & DUALHEAD_BITS) /*if some dualhead mode*/ 106 { 107 uint8 colour_depth2 = colour_depth1; 108 109 /* init display mode for secondary head */ 110 display_mode target2 = target; 111 112 LOG(1,("SETMODE: setting DUALHEAD mode\n")); 113 114 /* validate flags for secondary TVout */ 115 if ((i2c_sec_tv_adapter() != B_OK) && (target2.flags & TV_BITS)) 116 { 117 target.flags &= ~TV_BITS;//still needed for some routines... 118 target2.flags &= ~TV_BITS; 119 LOG(1,("SETMODE: blocking TVout: no TVout cable connected!\n")); 120 } 121 122 /* detect which connectors have a CRT connected */ 123 //fixme: 'hot-plugging' for analog monitors removed: remove code as well; 124 //or make it work with digital panels connected as well. 125 // crt1 = nv_dac_crt_connected(); 126 // crt2 = nv_dac2_crt_connected(); 127 /* connect outputs 'straight-through' */ 128 // if (crt1) 129 // { 130 /* connector1 is used as primary output */ 131 // cross = false; 132 // } 133 // else 134 // { 135 // if (crt2) 136 /* connector2 is used as primary output */ 137 // cross = true; 138 // else 139 /* no CRT detected: assume connector1 is used as primary output */ 140 // cross = false; 141 // } 142 /* set output connectors assignment if possible */ 143 if ((target.flags & DUALHEAD_BITS) == DUALHEAD_SWITCH) 144 /* invert output assignment in switch mode */ 145 nv_general_head_select(true); 146 else 147 nv_general_head_select(false); 148 149 /* set the pixel clock PLL(s) */ 150 LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock)); 151 if (head1_set_pix_pll(target) == B_ERROR) 152 LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n")); 153 154 /* we do not need to set the pixelclock here for a head that's in TVout mode */ 155 if (!(target2.flags & TV_BITS)) 156 { 157 LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock)); 158 if (head2_set_pix_pll(target2) == B_ERROR) 159 LOG(8,("SETMODE: error setting pixel clock (DAC2)\n")); 160 } 161 162 /*set the colour depth for CRTC1 and the DAC */ 163 switch(target.space) 164 { 165 case B_CMAP8: 166 colour_depth1 = 8; 167 head1_mode(BPP8, 1.0); 168 head1_depth(BPP8); 169 break; 170 case B_RGB15_LITTLE: 171 colour_depth1 = 16; 172 head1_mode(BPP15, 1.0); 173 head1_depth(BPP15); 174 break; 175 case B_RGB16_LITTLE: 176 colour_depth1 = 16; 177 head1_mode(BPP16, 1.0); 178 head1_depth(BPP16); 179 break; 180 case B_RGB32_LITTLE: 181 colour_depth1 = 32; 182 head1_mode(BPP32, 1.0); 183 head1_depth(BPP32); 184 break; 185 } 186 /*set the colour depth for CRTC2 and DAC2 */ 187 switch(target2.space) 188 { 189 case B_CMAP8: 190 colour_depth2 = 8; 191 head2_mode(BPP8, 1.0); 192 head2_depth(BPP8); 193 break; 194 case B_RGB15_LITTLE: 195 colour_depth2 = 16; 196 head2_mode(BPP15, 1.0); 197 head2_depth(BPP15); 198 break; 199 case B_RGB16_LITTLE: 200 colour_depth2 = 16; 201 head2_mode(BPP16, 1.0); 202 head2_depth(BPP16); 203 break; 204 case B_RGB32_LITTLE: 205 colour_depth2 = 32; 206 head2_mode(BPP32, 1.0); 207 head2_depth(BPP32); 208 break; 209 } 210 211 /* check if we are doing interlaced TVout mode */ 212 si->interlaced_tv_mode = false; 213 /* if ((target2.flags & TV_BITS) && (si->ps.card_type >= G450)) 214 si->interlaced_tv_mode = true; 215 */ 216 /*set the display(s) pitches*/ 217 head1_set_display_pitch (); 218 //fixme: seperate for real dualhead modes: 219 //we need a secondary si->fbc! 220 head2_set_display_pitch (); 221 222 /*work out where the "right" screen starts*/ 223 startadd_right = startadd + (target.timing.h_display * (colour_depth1 >> 3)); 224 225 /* Tell card what memory to display */ 226 switch (target.flags & DUALHEAD_BITS) 227 { 228 case DUALHEAD_ON: 229 case DUALHEAD_SWITCH: 230 head1_set_display_start(startadd,colour_depth1); 231 head2_set_display_start(startadd_right,colour_depth2); 232 break; 233 case DUALHEAD_CLONE: 234 head1_set_display_start(startadd,colour_depth1); 235 head2_set_display_start(startadd,colour_depth2); 236 break; 237 } 238 239 /* set the timing */ 240 head1_set_timing(target); 241 /* we do not need to setup CRTC2 here for a head that's in TVout mode */ 242 if (!(target2.flags & TV_BITS)) result = head2_set_timing(target2); 243 244 /* TVout support: setup CRTC2 and it's pixelclock */ 245 if (si->ps.tvout && (target2.flags & TV_BITS)) maventv_init(target2); 246 } 247 else /* single head mode */ 248 { 249 status_t status; 250 int colour_mode = BPP32; 251 252 /* connect output */ 253 if (si->ps.secondary_head) 254 { 255 /* detect which connectors have a CRT connected */ 256 //fixme: 'hot-plugging' for analog monitors removed: remove code as well; 257 //or make it work with digital panels connected as well. 258 // crt1 = nv_dac_crt_connected(); 259 // crt2 = nv_dac2_crt_connected(); 260 /* connect outputs 'straight-through' */ 261 // if (crt1) 262 // { 263 /* connector1 is used as primary output */ 264 // cross = false; 265 // } 266 // else 267 // { 268 // if (crt2) 269 /* connector2 is used as primary output */ 270 // cross = true; 271 // else 272 /* no CRT detected: assume connector1 is used as primary output */ 273 // cross = false; 274 // } 275 /* set output connectors assignment if possible */ 276 nv_general_head_select(false); 277 } 278 279 switch(target.space) 280 { 281 case B_CMAP8: colour_depth1 = 8; colour_mode = BPP8; break; 282 case B_RGB15_LITTLE: colour_depth1 = 16; colour_mode = BPP15; break; 283 case B_RGB16_LITTLE: colour_depth1 = 16; colour_mode = BPP16; break; 284 case B_RGB32_LITTLE: colour_depth1 = 32; colour_mode = BPP32; break; 285 default: 286 LOG(8,("SETMODE: Invalid singlehead colour depth 0x%08x\n", target.space)); 287 return B_ERROR; 288 } 289 290 /* set the pixel clock PLL */ 291 status = head1_set_pix_pll(target); 292 293 if (status==B_ERROR) 294 LOG(8,("CRTC: error setting pixel clock (internal DAC)\n")); 295 296 /* set the colour depth for CRTC1 and the DAC */ 297 /* first set the colordepth */ 298 head1_depth(colour_mode); 299 /* then(!) program the PAL (<8bit colordepth does not support 8bit PAL) */ 300 head1_mode(colour_mode,1.0); 301 302 /* set the display pitch */ 303 head1_set_display_pitch(); 304 305 /* tell the card what memory to display */ 306 head1_set_display_start(startadd,colour_depth1); 307 308 /* set the timing */ 309 head1_set_timing(target); 310 311 //fixme: shut-off the videoPLL if it exists... 312 } 313 314 /* update driver's mode store */ 315 si->dm = target; 316 317 /* update FIFO data fetching according to mode */ 318 nv_crtc_update_fifo(); 319 320 /* turn screen one on */ 321 head1_dpms(display, h, v); 322 /* turn screen two on if a dualhead mode is active */ 323 if (target.flags & DUALHEAD_BITS) head2_dpms(display,h,v); 324 325 /* set up acceleration for this mode */ 326 /* note: 327 * Maybe later we can forget about non-DMA mode (depends on 3D acceleration 328 * attempts). */ 329 if (!si->settings.dma_acc) 330 nv_acc_init(); 331 else 332 nv_acc_init_dma(); 333 /* set up overlay unit for this mode */ 334 nv_bes_init(); 335 336 /* note freemem range */ 337 /* first free adress follows hardcursor and workspace */ 338 si->engine.threeD.mem_low = si->fbc.bytes_per_row * si->dm.virtual_height; 339 if (si->settings.hardcursor) si->engine.threeD.mem_low += 2048; 340 /* last free adress is end-of-ram minus max space needed for overlay bitmaps */ 341 //fixme possible: 342 //if overlay buffers are allocated subtract buffersize from mem_high; 343 //only allocate overlay buffers if 3D is not in use. (block overlay during 3D) 344 si->engine.threeD.mem_high = si->ps.memory_size - 1; 345 /* don't touch the DMA acceleration engine command buffer if it exists */ 346 /* note: 347 * the buffer is 32kB in size. Keep some extra distance for safety (faulty apps). */ 348 if (si->settings.dma_acc) 349 { 350 if (si->ps.card_arch < NV40A) 351 { 352 /* keeping 32kB distance from the DMA buffer */ 353 si->engine.threeD.mem_high -= (64 * 1024); 354 } 355 else 356 { 357 /* 416kB distance is just OK: keeping another 64kB distance for safety; 358 * confirmed for NV43. */ 359 /* note: 360 * if you get too close to the DMA command buffer on NV40 and NV43 at 361 * least (both confirmed), the source DMA instance will mess-up for 362 * at least engine cmd NV_IMAGE_BLIT and NV12_IMAGE_BLIT. */ 363 si->engine.threeD.mem_high -= (512 * 1024); 364 } 365 } 366 si->engine.threeD.mem_high -= (MAXBUFFERS * 1024 * 1024 * 2); /* see overlay.c file */ 367 368 LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0)); 369 370 /* enable interrupts using the kernel driver */ 371 interrupt_enable(true); 372 373 /* make sure a possible 3D add-on will re-initialize itself by signalling ready */ 374 si->engine.threeD.mode_changing = false; 375 376 /* optimize memory-access if needed */ 377 // head1_mem_priority(colour_depth1); 378 379 /* Tune RAM CAS-latency if needed. Must be done *here*! */ 380 nv_set_cas_latency(); 381 382 return B_OK; 383 } 384 385 /* 386 Set which pixel of the virtual frame buffer will show up in the 387 top left corner of the display device. Used for page-flipping 388 games and virtual desktops. 389 */ 390 status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) { 391 uint8 colour_depth; 392 uint32 startadd,startadd_right; 393 394 LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start)); 395 396 /* nVidia cards support pixelprecise panning on both heads in all modes: 397 * No stepping granularity needed! */ 398 399 /* determine bits used for the colordepth */ 400 switch(si->dm.space) 401 { 402 case B_CMAP8: 403 colour_depth=8; 404 break; 405 case B_RGB15_LITTLE: 406 case B_RGB16_LITTLE: 407 colour_depth=16; 408 break; 409 case B_RGB24_LITTLE: 410 colour_depth=24; 411 break; 412 case B_RGB32_LITTLE: 413 colour_depth=32; 414 break; 415 default: 416 return B_ERROR; 417 } 418 419 /* do not run past end of display */ 420 switch (si->dm.flags & DUALHEAD_BITS) 421 { 422 case DUALHEAD_ON: 423 case DUALHEAD_SWITCH: 424 if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width) 425 return B_ERROR; 426 break; 427 default: 428 if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width) 429 return B_ERROR; 430 break; 431 } 432 if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height) 433 return B_ERROR; 434 435 /* everybody remember where we parked... */ 436 si->dm.h_display_start = h_display_start; 437 si->dm.v_display_start = v_display_start; 438 439 /* actually set the registers */ 440 //fixme: seperate both heads: we need a secondary si->fbc! 441 startadd = v_display_start * si->fbc.bytes_per_row; 442 startadd += h_display_start * (colour_depth >> 3); 443 startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer; 444 startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3); 445 446 interrupt_enable(false); 447 448 switch (si->dm.flags & DUALHEAD_BITS) 449 { 450 case DUALHEAD_ON: 451 case DUALHEAD_SWITCH: 452 head1_set_display_start(startadd,colour_depth); 453 head2_set_display_start(startadd_right,colour_depth); 454 break; 455 case DUALHEAD_OFF: 456 head1_set_display_start(startadd,colour_depth); 457 break; 458 case DUALHEAD_CLONE: 459 head1_set_display_start(startadd,colour_depth); 460 head2_set_display_start(startadd,colour_depth); 461 break; 462 } 463 464 interrupt_enable(true); 465 return B_OK; 466 } 467 468 /* Set the indexed color palette */ 469 void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) { 470 int i; 471 uint8 *r,*g,*b; 472 473 /* Protect gamma correction when not in CMAP8 */ 474 if (si->dm.space != B_CMAP8) return; 475 476 r=si->color_data; 477 g=r+256; 478 b=g+256; 479 480 i=first; 481 while (count--) 482 { 483 r[i]=*color_data++; 484 g[i]=*color_data++; 485 b[i]=*color_data++; 486 i++; 487 } 488 head1_palette(r,g,b); 489 if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b); 490 } 491 492 /* Put the display into one of the Display Power Management modes. */ 493 status_t SET_DPMS_MODE(uint32 dpms_flags) { 494 interrupt_enable(false); 495 496 LOG(4,("SET_DPMS_MODE: 0x%08x\n", dpms_flags)); 497 498 if (si->dm.flags & DUALHEAD_BITS) /*dualhead*/ 499 { 500 switch(dpms_flags) 501 { 502 case B_DPMS_ON: /* H: on, V: on, display on */ 503 head1_dpms(true, true, true); 504 if (si->ps.secondary_head) head2_dpms(true, true, true); 505 break; 506 case B_DPMS_STAND_BY: 507 head1_dpms(false, false, true); 508 if (si->ps.secondary_head) head2_dpms(false, false, true); 509 break; 510 case B_DPMS_SUSPEND: 511 head1_dpms(false, true, false); 512 if (si->ps.secondary_head) head2_dpms(false, true, false); 513 break; 514 case B_DPMS_OFF: /* H: off, V: off, display off */ 515 head1_dpms(false, false, false); 516 if (si->ps.secondary_head) head2_dpms(false, false, false); 517 break; 518 default: 519 LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags)); 520 interrupt_enable(true); 521 return B_ERROR; 522 } 523 } 524 else /* singlehead */ 525 { 526 switch(dpms_flags) 527 { 528 case B_DPMS_ON: /* H: on, V: on, display on */ 529 head1_dpms(true, true, true); 530 break; 531 case B_DPMS_STAND_BY: 532 head1_dpms(false, false, true); 533 break; 534 case B_DPMS_SUSPEND: 535 head1_dpms(false, true, false); 536 break; 537 case B_DPMS_OFF: /* H: off, V: off, display off */ 538 head1_dpms(false, false, false); 539 break; 540 default: 541 LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags)); 542 interrupt_enable(true); 543 return B_ERROR; 544 } 545 } 546 interrupt_enable(true); 547 return B_OK; 548 } 549 550 /* Report device DPMS capabilities */ 551 uint32 DPMS_CAPABILITIES(void) { 552 return (B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF); 553 } 554 555 /* Return the current DPMS mode */ 556 uint32 DPMS_MODE(void) { 557 bool display, h, v; 558 559 interrupt_enable(false); 560 head1_dpms_fetch(&display, &h, &v); 561 interrupt_enable(true); 562 563 if (display && h && v) 564 return B_DPMS_ON; 565 else if(v) 566 return B_DPMS_STAND_BY; 567 else if(h) 568 return B_DPMS_SUSPEND; 569 else 570 return B_DPMS_OFF; 571 } 572