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-4/2004 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 eng_set_bool_state sbs; 23 24 /* set the magic number so the driver knows we're for real */ 25 sbs.magic = SKEL_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, ENG_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 /* disable interrupts using the kernel driver */ 81 interrupt_enable(false); 82 83 /* find current DPMS state, then turn off screen(s) */ 84 // head1_dpms_fetch(&display, &h, &v); 85 // head1_dpms(false, false, false); 86 // if (si->ps.secondary_head) head2_dpms(false, false, false); 87 88 /*where in framebuffer the screen is (should this be dependant on previous MOVEDISPLAY?)*/ 89 startadd = (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer; 90 91 /* calculate and set new mode bytes_per_row */ 92 eng_general_validate_pic_size (&target, &si->fbc.bytes_per_row, &si->acc_mode); 93 94 /*Perform the very long mode switch!*/ 95 if (target.flags & DUALHEAD_BITS) /*if some dualhead mode*/ 96 { 97 uint8 colour_depth2 = colour_depth1; 98 99 /* init display mode for secondary head */ 100 display_mode target2 = target; 101 102 LOG(1,("SETMODE: setting DUALHEAD mode\n")); 103 104 /* validate flags for secondary TVout */ 105 // if ((i2c_sec_tv_adapter() != B_OK) && (target2.flags & TV_BITS)) 106 // { 107 // target.flags &= ~TV_BITS;//still needed for some routines... 108 // target2.flags &= ~TV_BITS; 109 // LOG(1,("SETMODE: blocking TVout: no TVout cable connected!\n")); 110 // } 111 112 /* detect which connectors have a CRT connected */ 113 //fixme: 'hot-plugging' for analog monitors removed: remove code as well; 114 //or make it work with digital panels connected as well. 115 // crt1 = eng_dac_crt_connected(); 116 // crt2 = eng_dac2_crt_connected(); 117 /* connect outputs 'straight-through' */ 118 // if (crt1) 119 // { 120 /* connector1 is used as primary output */ 121 // cross = false; 122 // } 123 // else 124 // { 125 // if (crt2) 126 /* connector2 is used as primary output */ 127 // cross = true; 128 // else 129 /* no CRT detected: assume connector1 is used as primary output */ 130 // cross = false; 131 // } 132 /* set output connectors assignment if possible */ 133 // if ((target.flags & DUALHEAD_BITS) == DUALHEAD_SWITCH) 134 /* invert output assignment in switch mode */ 135 // eng_general_head_select(true); 136 // else 137 // eng_general_head_select(false); 138 139 /* set the pixel clock PLL(s) */ 140 LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock)); 141 // if (head1_set_pix_pll(target) == B_ERROR) 142 // LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n")); 143 144 /* we do not need to set the pixelclock here for a head that's in TVout mode */ 145 // if (!(target2.flags & TV_BITS)) 146 // { 147 // LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock)); 148 // if (head2_set_pix_pll(target2) == B_ERROR) 149 // LOG(8,("SETMODE: error setting pixel clock (DAC2)\n")); 150 // } 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 si->interlaced_tv_mode = false; 203 /* if ((target2.flags & TV_BITS) && (si->ps.card_type >= G450)) 204 si->interlaced_tv_mode = true; 205 */ 206 /*set the display(s) pitches*/ 207 // head1_set_display_pitch (); 208 //fixme: seperate for real dualhead modes: 209 //we need a secondary si->fbc! 210 // head2_set_display_pitch (); 211 212 /*work out where the "right" screen starts*/ 213 startadd_right = startadd + (target.timing.h_display * (colour_depth1 >> 3)); 214 215 /* Tell card what memory to display */ 216 switch (target.flags & DUALHEAD_BITS) 217 { 218 case DUALHEAD_ON: 219 case DUALHEAD_SWITCH: 220 // head1_set_display_start(startadd,colour_depth1); 221 // head2_set_display_start(startadd_right,colour_depth2); 222 break; 223 case DUALHEAD_CLONE: 224 // head1_set_display_start(startadd,colour_depth1); 225 // head2_set_display_start(startadd,colour_depth2); 226 break; 227 } 228 229 /* set the timing */ 230 // head1_set_timing(target); 231 /* we do not need to setup CRTC2 here for a head that's in TVout mode */ 232 // if (!(target2.flags & TV_BITS)) result = head2_set_timing(target2); 233 234 /* TVout support: setup CRTC2 and it's pixelclock */ 235 // if (si->ps.tvout && (target2.flags & TV_BITS)) maventv_init(target2); 236 } 237 else /* single head mode */ 238 { 239 status_t status; 240 int colour_mode = BPP32; 241 242 /* connect output */ 243 if (si->ps.secondary_head) 244 { 245 /* detect which connectors have a CRT connected */ 246 //fixme: 'hot-plugging' for analog monitors removed: remove code as well; 247 //or make it work with digital panels connected as well. 248 // crt1 = eng_dac_crt_connected(); 249 // crt2 = eng_dac2_crt_connected(); 250 /* connect outputs 'straight-through' */ 251 // if (crt1) 252 // { 253 /* connector1 is used as primary output */ 254 // cross = false; 255 // } 256 // else 257 // { 258 // if (crt2) 259 /* connector2 is used as primary output */ 260 // cross = true; 261 // else 262 /* no CRT detected: assume connector1 is used as primary output */ 263 // cross = false; 264 // } 265 /* set output connectors assignment if possible */ 266 eng_general_head_select(false); 267 } 268 269 switch(target.space) 270 { 271 case B_CMAP8: colour_depth1 = 8; colour_mode = BPP8; break; 272 case B_RGB15_LITTLE: colour_depth1 = 16; colour_mode = BPP15; break; 273 case B_RGB16_LITTLE: colour_depth1 = 16; colour_mode = BPP16; break; 274 case B_RGB32_LITTLE: colour_depth1 = 32; colour_mode = BPP32; break; 275 default: 276 LOG(8,("SETMODE: Invalid singlehead colour depth 0x%08x\n", target.space)); 277 return B_ERROR; 278 } 279 280 /* set the pixel clock PLL */ 281 // status = head1_set_pix_pll(target); 282 status = B_OK; 283 284 if (status==B_ERROR) 285 LOG(8,("CRTC: error setting pixel clock (internal DAC)\n")); 286 287 /* set the colour depth for CRTC1 and the DAC */ 288 /* first set the colordepth */ 289 // head1_depth(colour_mode); 290 /* then(!) program the PAL (<8bit colordepth does not support 8bit PAL) */ 291 // head1_mode(colour_mode,1.0); 292 293 /* set the display pitch */ 294 // head1_set_display_pitch(); 295 296 /* tell the card what memory to display */ 297 // head1_set_display_start(startadd,colour_depth1); 298 299 /* set the timing */ 300 // head1_set_timing(target); 301 302 //fixme: shut-off the videoPLL if it exists... 303 } 304 305 /* update driver's mode store */ 306 si->dm = target; 307 308 /* turn screen one on */ 309 // head1_dpms(display, h, v); 310 /* turn screen two on if a dualhead mode is active */ 311 // if (target.flags & DUALHEAD_BITS) head2_dpms(display,h,v); 312 313 /* set up acceleration for this mode */ 314 // eng_acc_init(); 315 /* set up overlay unit for this mode */ 316 // eng_bes_init(); 317 318 LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0)); 319 320 /* enable interrupts using the kernel driver */ 321 interrupt_enable(true); 322 323 /* optimize memory-access if needed */ 324 // head1_mem_priority(colour_depth1); 325 326 /* Tune RAM CAS-latency if needed. Must be done *here*! */ 327 eng_set_cas_latency(); 328 329 return B_OK; 330 } 331 332 /* 333 Set which pixel of the virtual frame buffer will show up in the 334 top left corner of the display device. Used for page-flipping 335 games and virtual desktops. 336 */ 337 status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) { 338 uint8 colour_depth; 339 uint32 startadd,startadd_right; 340 341 LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start)); 342 343 /* nVidia cards support pixelprecise panning on both heads in all modes: 344 * No stepping granularity needed! */ 345 346 /* determine bits used for the colordepth */ 347 switch(si->dm.space) 348 { 349 case B_CMAP8: 350 colour_depth=8; 351 break; 352 case B_RGB15_LITTLE: 353 case B_RGB16_LITTLE: 354 colour_depth=16; 355 break; 356 case B_RGB24_LITTLE: 357 colour_depth=24; 358 break; 359 case B_RGB32_LITTLE: 360 colour_depth=32; 361 break; 362 default: 363 return B_ERROR; 364 } 365 366 /* do not run past end of display */ 367 switch (si->dm.flags & DUALHEAD_BITS) 368 { 369 case DUALHEAD_ON: 370 case DUALHEAD_SWITCH: 371 if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width) 372 return B_ERROR; 373 break; 374 default: 375 if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width) 376 return B_ERROR; 377 break; 378 } 379 if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height) 380 return B_ERROR; 381 382 /* everybody remember where we parked... */ 383 si->dm.h_display_start = h_display_start; 384 si->dm.v_display_start = v_display_start; 385 386 /* actually set the registers */ 387 //fixme: seperate both heads: we need a secondary si->fbc! 388 startadd = v_display_start * si->fbc.bytes_per_row; 389 startadd += h_display_start * (colour_depth >> 3); 390 startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer; 391 startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3); 392 393 interrupt_enable(false); 394 395 switch (si->dm.flags & DUALHEAD_BITS) 396 { 397 case DUALHEAD_ON: 398 case DUALHEAD_SWITCH: 399 // head1_set_display_start(startadd,colour_depth); 400 // head2_set_display_start(startadd_right,colour_depth); 401 break; 402 case DUALHEAD_OFF: 403 // head1_set_display_start(startadd,colour_depth); 404 break; 405 case DUALHEAD_CLONE: 406 // head1_set_display_start(startadd,colour_depth); 407 // head2_set_display_start(startadd,colour_depth); 408 break; 409 } 410 411 interrupt_enable(true); 412 return B_OK; 413 } 414 415 /* Set the indexed color palette */ 416 void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) { 417 int i; 418 uint8 *r,*g,*b; 419 420 /* Protect gamma correction when not in CMAP8 */ 421 if (si->dm.space != B_CMAP8) return; 422 423 r=si->color_data; 424 g=r+256; 425 b=g+256; 426 427 i=first; 428 while (count--) 429 { 430 r[i]=*color_data++; 431 g[i]=*color_data++; 432 b[i]=*color_data++; 433 i++; 434 } 435 // head1_palette(r,g,b); 436 if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b); 437 } 438 439 /* Put the display into one of the Display Power Management modes. */ 440 status_t SET_DPMS_MODE(uint32 dpms_flags) { 441 interrupt_enable(false); 442 443 LOG(4,("SET_DPMS_MODE: 0x%08x\n", dpms_flags)); 444 445 if (si->dm.flags & DUALHEAD_BITS) /*dualhead*/ 446 { 447 switch(dpms_flags) 448 { 449 case B_DPMS_ON: /* H: on, V: on, display on */ 450 // head1_dpms(true, true, true); 451 // if (si->ps.secondary_head) head2_dpms(true, true, true); 452 break; 453 case B_DPMS_STAND_BY: 454 // head1_dpms(false, false, true); 455 // if (si->ps.secondary_head) head2_dpms(false, false, true); 456 break; 457 case B_DPMS_SUSPEND: 458 // head1_dpms(false, true, false); 459 // if (si->ps.secondary_head) head2_dpms(false, true, false); 460 break; 461 case B_DPMS_OFF: /* H: off, V: off, display off */ 462 // head1_dpms(false, false, false); 463 // if (si->ps.secondary_head) head2_dpms(false, false, false); 464 break; 465 default: 466 LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags)); 467 interrupt_enable(true); 468 return B_ERROR; 469 } 470 } 471 else /* singlehead */ 472 { 473 switch(dpms_flags) 474 { 475 case B_DPMS_ON: /* H: on, V: on, display on */ 476 // head1_dpms(true, true, true); 477 break; 478 case B_DPMS_STAND_BY: 479 // head1_dpms(false, false, true); 480 break; 481 case B_DPMS_SUSPEND: 482 // head1_dpms(false, true, false); 483 break; 484 case B_DPMS_OFF: /* H: off, V: off, display off */ 485 // head1_dpms(false, false, false); 486 break; 487 default: 488 LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags)); 489 interrupt_enable(true); 490 return B_ERROR; 491 } 492 } 493 interrupt_enable(true); 494 return B_OK; 495 } 496 497 /* Report device DPMS capabilities */ 498 uint32 DPMS_CAPABILITIES(void) { 499 return (B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF); 500 } 501 502 /* Return the current DPMS mode */ 503 uint32 DPMS_MODE(void) { 504 bool display, h, v; 505 506 interrupt_enable(false); 507 // head1_dpms_fetch(&display, &h, &v); 508 display = h = v = true; 509 510 interrupt_enable(true); 511 512 if (display && h && v) 513 return B_DPMS_ON; 514 else if(v) 515 return B_DPMS_STAND_BY; 516 else if(h) 517 return B_DPMS_SUSPEND; 518 else 519 return B_DPMS_OFF; 520 } 521