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/2008 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)! */ 17status_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 /* turn off screen(s) */ 66// head1_dpms(false, false, false, true); 67// if (si->ps.secondary_head) head2_dpms(false, false, false, true); 68 69 /*where in framebuffer the screen is (should this be dependant on previous MOVEDISPLAY?)*/ 70 startadd = (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer; 71 72 /* calculate and set new mode bytes_per_row */ 73 nv_general_validate_pic_size (&target, &si->fbc.bytes_per_row, &si->acc_mode); 74 75 /*Perform the very long mode switch!*/ 76 if (target.flags & DUALHEAD_BITS) /*if some dualhead mode*/ 77 { 78 uint8 colour_depth2 = colour_depth1; 79 80 /* init display mode for secondary head */ 81 display_mode target2 = target; 82 83 LOG(1,("SETMODE: setting DUALHEAD mode\n")); 84 85 /* detect which connectors have a CRT connected */ 86 //fixme: 'hot-plugging' for analog monitors removed: remove code as well; 87 //or make it work with digital panels connected as well. 88// crt1 = nv_dac_crt_connected(); 89// crt2 = nv_dac2_crt_connected(); 90 /* connect outputs 'straight-through' */ 91// if (crt1) 92// { 93 /* connector1 is used as primary output */ 94// cross = false; 95// } 96// else 97// { 98// if (crt2) 99 /* connector2 is used as primary output */ 100// cross = true; 101// else 102 /* no CRT detected: assume connector1 is used as primary output */ 103// cross = false; 104// } 105 /* set output connectors assignment if possible */ 106// if ((target.flags & DUALHEAD_BITS) == DUALHEAD_SWITCH) 107 /* invert output assignment in switch mode */ 108// nv_general_head_select(true); 109// else 110// nv_general_head_select(false); 111 112 /* set the pixel clock PLL(s) */ 113 LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock)); 114// if (head1_set_pix_pll(target) == B_ERROR) 115// LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n")); 116 117 LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock)); 118// if (head2_set_pix_pll(target2) == B_ERROR) 119// LOG(8,("SETMODE: error setting pixel clock (DAC2)\n")); 120 121 /*set the colour depth for CRTC1 and the DAC */ 122 switch(target.space) 123 { 124 case B_CMAP8: 125 colour_depth1 = 8; 126// head1_mode(BPP8, 1.0); 127// head1_depth(BPP8); 128 break; 129 case B_RGB15_LITTLE: 130 colour_depth1 = 16; 131// head1_mode(BPP15, 1.0); 132// head1_depth(BPP15); 133 break; 134 case B_RGB16_LITTLE: 135 colour_depth1 = 16; 136// head1_mode(BPP16, 1.0); 137// head1_depth(BPP16); 138 break; 139 case B_RGB32_LITTLE: 140 colour_depth1 = 32; 141// head1_mode(BPP32, 1.0); 142// head1_depth(BPP32); 143 break; 144 } 145 /*set the colour depth for CRTC2 and DAC2 */ 146 switch(target2.space) 147 { 148 case B_CMAP8: 149 colour_depth2 = 8; 150// head2_mode(BPP8, 1.0); 151// head2_depth(BPP8); 152 break; 153 case B_RGB15_LITTLE: 154 colour_depth2 = 16; 155// head2_mode(BPP15, 1.0); 156// head2_depth(BPP15); 157 break; 158 case B_RGB16_LITTLE: 159 colour_depth2 = 16; 160// head2_mode(BPP16, 1.0); 161// head2_depth(BPP16); 162 break; 163 case B_RGB32_LITTLE: 164 colour_depth2 = 32; 165// head2_mode(BPP32, 1.0); 166// head2_depth(BPP32); 167 break; 168 } 169 170 /*set the display(s) pitches*/ 171// head1_set_display_pitch (); 172 //fixme: seperate for real dualhead modes: 173 //we need a secondary si->fbc! 174// head2_set_display_pitch (); 175 176 /*work out where the "right" screen starts*/ 177 startadd_right = startadd + (target.timing.h_display * (colour_depth1 >> 3)); 178 179 /* Tell card what memory to display */ 180 switch (target.flags & DUALHEAD_BITS) 181 { 182 case DUALHEAD_ON: 183 case DUALHEAD_SWITCH: 184// head1_set_display_start(startadd,colour_depth1); 185// head2_set_display_start(startadd_right,colour_depth2); 186 break; 187 case DUALHEAD_CLONE: 188// head1_set_display_start(startadd,colour_depth1); 189// head2_set_display_start(startadd,colour_depth2); 190 break; 191 } 192 193 /* set the timing */ 194// head1_set_timing(target); 195// head2_set_timing(target2); 196 } 197 else /* single head mode */ 198 { 199 int colour_mode = BPP32; 200 201 /* connect output */ 202 if (si->ps.secondary_head) 203 { 204 /* detect which connectors have a CRT connected */ 205 //fixme: 'hot-plugging' for analog monitors removed: remove code as well; 206 //or make it work with digital panels connected as well. 207// crt1 = nv_dac_crt_connected(); 208// crt2 = nv_dac2_crt_connected(); 209 /* connect outputs 'straight-through' */ 210// if (crt1) 211// { 212 /* connector1 is used as primary output */ 213// cross = false; 214// } 215// else 216// { 217// if (crt2) 218 /* connector2 is used as primary output */ 219// cross = true; 220// else 221 /* no CRT detected: assume connector1 is used as primary output */ 222// cross = false; 223// } 224 /* set output connectors assignment if possible */ 225 nv_general_head_select(false); 226 } 227 228 switch(target.space) 229 { 230 case B_CMAP8: colour_depth1 = 8; colour_mode = BPP8; break; 231 case B_RGB15_LITTLE: colour_depth1 = 16; colour_mode = BPP15; break; 232 case B_RGB16_LITTLE: colour_depth1 = 16; colour_mode = BPP16; break; 233 case B_RGB32_LITTLE: colour_depth1 = 32; colour_mode = BPP32; break; 234 default: 235 LOG(8,("SETMODE: Invalid singlehead colour depth 0x%08x\n", target.space)); 236 return B_ERROR; 237 } 238 239 /* set the pixel clock PLL */ 240// if (head1_set_pix_pll(target) == B_ERROR) 241// LOG(8,("CRTC: error setting pixel clock (internal DAC)\n")); 242 243 /* set the colour depth for CRTC1 and the DAC */ 244 /* first set the colordepth */ 245// head1_depth(colour_mode); 246 /* then(!) program the PAL (<8bit colordepth does not support 8bit PAL) */ 247// head1_mode(colour_mode,1.0); 248 249 /* set the display pitch */ 250// head1_set_display_pitch(); 251 252 /* tell the card what memory to display */ 253// head1_set_display_start(startadd,colour_depth1); 254 255 /* set the timing */ 256// head1_set_timing(target); 257 258 //fixme: shut-off the videoPLL if it exists... 259 } 260 261 /* update driver's mode store */ 262 si->dm = target; 263 264 /* update FIFO data fetching according to mode */ 265// nv_crtc_update_fifo(); 266// if (si->ps.secondary_head) nv_crtc2_update_fifo(); 267 268 /* set up acceleration for this mode */ 269 /* note: 270 * Maybe later we can forget about non-DMA mode (depends on 3D acceleration 271 * attempts). */ 272//no acc support for G8x yet! 273if (si->ps.card_arch < NV50A) 274{ 275 nv_acc_init_dma(); 276} 277 /* set up overlay unit for this mode */ 278// nv_bes_init(); 279 280 /* note freemem range */ 281 /* first free adress follows hardcursor and workspace */ 282 si->engine.threeD.mem_low = si->fbc.bytes_per_row * si->dm.virtual_height; 283 if (si->settings.hardcursor) si->engine.threeD.mem_low += 2048; 284 /* last free adress is end-of-ram minus max space needed for overlay bitmaps */ 285 //fixme possible: 286 //if overlay buffers are allocated subtract buffersize from mem_high; 287 //only allocate overlay buffers if 3D is not in use. (block overlay during 3D) 288 si->engine.threeD.mem_high = si->ps.memory_size - 1; 289 /* Keep some extra distance as a workaround for certain bugs (see 290 * DriverInterface.h for an explanation). */ 291 si->engine.threeD.mem_high -= NV40_PLUS_OFFSET; 292 293 /* restore screen(s) output state(s) */ 294// SET_DPMS_MODE(si->dpms_flags); 295 296 /* enable interrupts using the kernel driver */ 297 //fixme: 298 //add head2 once we use one driver instance 'per head' (instead of 'per card') 299// head1_interrupt_enable(true); 300 301 /* make sure a possible 3D add-on will re-initialize itself by signalling ready */ 302 si->engine.threeD.mode_changing = false; 303 304 /* optimize memory-access if needed */ 305// head1_mem_priority(colour_depth1); 306 307 /* Tune RAM CAS-latency if needed. Must be done *here*! */ 308// nv_set_cas_latency(); 309 310 LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0)); 311 312 return B_OK; 313} 314 315/* 316 Set which pixel of the virtual frame buffer will show up in the 317 top left corner of the display device. Used for page-flipping 318 games and virtual desktops. 319*/ 320status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) { 321 uint8 colour_depth; 322 uint32 startadd,startadd_right; 323 324 LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start)); 325 326 /* nVidia cards support pixelprecise panning on both heads in all modes: 327 * No stepping granularity needed! */ 328 329 /* determine bits used for the colordepth */ 330 switch(si->dm.space) 331 { 332 case B_CMAP8: 333 colour_depth=8; 334 break; 335 case B_RGB15_LITTLE: 336 case B_RGB16_LITTLE: 337 colour_depth=16; 338 break; 339 case B_RGB24_LITTLE: 340 colour_depth=24; 341 break; 342 case B_RGB32_LITTLE: 343 colour_depth=32; 344 break; 345 default: 346 return B_ERROR; 347 } 348 349 /* do not run past end of display */ 350 switch (si->dm.flags & DUALHEAD_BITS) 351 { 352 case DUALHEAD_ON: 353 case DUALHEAD_SWITCH: 354 if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width) 355 return B_ERROR; 356 break; 357 default: 358 if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width) 359 return B_ERROR; 360 break; 361 } 362 if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height) 363 return B_ERROR; 364 365 /* everybody remember where we parked... */ 366 si->dm.h_display_start = h_display_start; 367 si->dm.v_display_start = v_display_start; 368 369 /* actually set the registers */ 370 //fixme: seperate both heads: we need a secondary si->fbc! 371 startadd = v_display_start * si->fbc.bytes_per_row; 372 startadd += h_display_start * (colour_depth >> 3); 373 startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer; 374 startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3); 375 376 /* disable interrupts using the kernel driver */ 377 head1_interrupt_enable(false); 378 if (si->ps.secondary_head) head2_interrupt_enable(false); 379 380 switch (si->dm.flags & DUALHEAD_BITS) 381 { 382 case DUALHEAD_ON: 383 case DUALHEAD_SWITCH: 384 head1_set_display_start(startadd,colour_depth); 385 head2_set_display_start(startadd_right,colour_depth); 386 break; 387 case DUALHEAD_OFF: 388 head1_set_display_start(startadd,colour_depth); 389 break; 390 case DUALHEAD_CLONE: 391 head1_set_display_start(startadd,colour_depth); 392 head2_set_display_start(startadd,colour_depth); 393 break; 394 } 395 396 //fixme: 397 //add head2 once we use one driver instance 'per head' (instead of 'per card') 398 head1_interrupt_enable(true); 399 400 return B_OK; 401} 402 403/* Set the indexed color palette */ 404void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) { 405 int i; 406 uint8 *r,*g,*b; 407 408 /* Protect gamma correction when not in CMAP8 */ 409 if (si->dm.space != B_CMAP8) return; 410 411 r=si->color_data; 412 g=r+256; 413 b=g+256; 414 415 i=first; 416 while (count--) 417 { 418 r[i]=*color_data++; 419 g[i]=*color_data++; 420 b[i]=*color_data++; 421 i++; 422 } 423 head1_palette(r,g,b); 424 if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b); 425} 426 427/* Put the display into one of the Display Power Management modes. */ 428status_t SET_DPMS_MODE(uint32 dpms_flags) 429{ 430 bool display, h1h, h1v, h2h, h2v, do_p1, do_p2; 431 432 /* disable interrupts using the kernel driver */ 433 head1_interrupt_enable(false); 434 if (si->ps.secondary_head) head2_interrupt_enable(false); 435 436 LOG(4,("SET_DPMS_MODE: $%08x\n", dpms_flags)); 437 438 /* note current DPMS state for our reference */ 439 si->dpms_flags = dpms_flags; 440 441 /* preset: DPMS for panels should be executed */ 442 do_p1 = do_p2 = true; 443 444 /* determine signals to send to head(s) */ 445 display = h1h = h1v = h2h = h2v = true; 446 switch(dpms_flags) 447 { 448 case B_DPMS_ON: /* H: on, V: on, display on */ 449 break; 450 case B_DPMS_STAND_BY: 451 display = h1h = h2h = false; 452 break; 453 case B_DPMS_SUSPEND: 454 display = h1v = h2v = false; 455 break; 456 case B_DPMS_OFF: /* H: off, V: off, display off */ 457 display = h1h = h1v = h2h = h2v = false; 458 break; 459 default: 460 LOG(8,("SET: Invalid DPMS settings $%08x\n", dpms_flags)); 461 //fixme: 462 //add head2 once we use one driver instance 'per head' (instead of 'per card') 463 head1_interrupt_enable(true); 464 465 return B_ERROR; 466 } 467 468 /* issue actual DPMS commands as far as applicable */ 469 head1_dpms(display, h1h, h1v, do_p1); 470 if ((si->ps.secondary_head) && (si->dm.flags & DUALHEAD_BITS)) 471 head2_dpms(display, h2h, h2v, do_p2); 472 473 //fixme: 474 //add head2 once we use one driver instance 'per head' (instead of 'per card') 475 head1_interrupt_enable(true); 476 477 return B_OK; 478} 479 480/* Report device DPMS capabilities */ 481uint32 DPMS_CAPABILITIES(void) 482{ 483 return (B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF); 484} 485 486/* Return the current DPMS mode */ 487uint32 DPMS_MODE(void) 488{ 489 return si->dpms_flags; 490} 491