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-9/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 eng_set_bool_state sbs; 23 24 /* set the magic number so the driver knows we're for real */ 25 sbs.magic = VIA_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 283 if (status==B_ERROR) 284 LOG(8,("CRTC: error setting pixel clock (internal DAC)\n")); 285 286 /* set the colour depth for CRTC1 and the DAC */ 287 /* first set the colordepth */ 288 head1_depth(colour_mode); 289 /* then(!) program the PAL (<8bit colordepth does not support 8bit PAL) */ 290 head1_mode(colour_mode,1.0); 291 292 /* set the display pitch */ 293 head1_set_display_pitch(); 294 295 /* tell the card what memory to display */ 296 head1_set_display_start(startadd,colour_depth1); 297 298 /* set the timing */ 299 head1_set_timing(target); 300 301 //fixme: shut-off the videoPLL if it exists... 302 } 303 304 /* update driver's mode store */ 305 si->dm = target; 306 307 /* turn screen one on */ 308 head1_dpms(display, h, v); 309 /* turn screen two on if a dualhead mode is active */ 310 // if (target.flags & DUALHEAD_BITS) head2_dpms(display,h,v); 311 312 /* set up acceleration for this mode */ 313 // eng_acc_init(); 314 /* set up overlay unit for this mode */ 315 eng_bes_init(); 316 317 LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0)); 318 319 /* enable interrupts using the kernel driver */ 320 interrupt_enable(true); 321 322 /* optimize memory-access if needed */ 323 // head1_mem_priority(colour_depth1); 324 325 /* Tune RAM CAS-latency if needed. Must be done *here*! */ 326 eng_set_cas_latency(); 327 328 return B_OK; 329 } 330 331 /* 332 Set which pixel of the virtual frame buffer will show up in the 333 top left corner of the display device. Used for page-flipping 334 games and virtual desktops. 335 */ 336 status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) { 337 uint8 colour_depth; 338 uint32 startadd,startadd_right; 339 340 LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start)); 341 342 /* VIA CRTC1 handles multiples of 8 for 8bit, 4 for 16bit, 2 for 32 bit 343 VIA CRTC2 is yet unknown... 344 */ 345 346 /* reset lower bits, don't return an error! */ 347 if (si->dm.flags & DUALHEAD_BITS) 348 { 349 //fixme for VIA... 350 switch(si->dm.space) 351 { 352 case B_RGB16_LITTLE: 353 colour_depth=16; 354 h_display_start &= ~0x1f; 355 break; 356 case B_RGB32_LITTLE: 357 colour_depth=32; 358 h_display_start &= ~0x0f; 359 break; 360 default: 361 LOG(8,("SET:Invalid DH colour depth 0x%08x, should never happen\n", si->dm.space)); 362 return B_ERROR; 363 } 364 } 365 else 366 { 367 switch(si->dm.space) 368 { 369 case B_CMAP8: 370 colour_depth=8; 371 h_display_start &= ~0x07; 372 break; 373 case B_RGB15_LITTLE: case B_RGB16_LITTLE: 374 colour_depth=16; 375 h_display_start &= ~0x03; 376 break; 377 case B_RGB32_LITTLE: 378 colour_depth=32; 379 h_display_start &= ~0x01; 380 break; 381 default: 382 return B_ERROR; 383 } 384 } 385 386 /* do not run past end of display */ 387 switch (si->dm.flags & DUALHEAD_BITS) 388 { 389 case DUALHEAD_ON: 390 case DUALHEAD_SWITCH: 391 if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width) 392 return B_ERROR; 393 break; 394 default: 395 if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width) 396 return B_ERROR; 397 break; 398 } 399 if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height) 400 return B_ERROR; 401 402 /* everybody remember where we parked... */ 403 si->dm.h_display_start = h_display_start; 404 si->dm.v_display_start = v_display_start; 405 406 /* actually set the registers */ 407 //fixme: seperate both heads: we need a secondary si->fbc! 408 startadd = v_display_start * si->fbc.bytes_per_row; 409 startadd += h_display_start * (colour_depth >> 3); 410 startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer; 411 startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3); 412 413 interrupt_enable(false); 414 415 switch (si->dm.flags & DUALHEAD_BITS) 416 { 417 case DUALHEAD_ON: 418 case DUALHEAD_SWITCH: 419 head1_set_display_start(startadd,colour_depth); 420 // head2_set_display_start(startadd_right,colour_depth); 421 break; 422 case DUALHEAD_OFF: 423 head1_set_display_start(startadd,colour_depth); 424 break; 425 case DUALHEAD_CLONE: 426 head1_set_display_start(startadd,colour_depth); 427 // head2_set_display_start(startadd,colour_depth); 428 break; 429 } 430 431 interrupt_enable(true); 432 return B_OK; 433 } 434 435 /* Set the indexed color palette */ 436 void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) { 437 int i; 438 uint8 *r,*g,*b; 439 440 /* Protect gamma correction when not in CMAP8 */ 441 if (si->dm.space != B_CMAP8) return; 442 443 r=si->color_data; 444 g=r+256; 445 b=g+256; 446 447 i=first; 448 while (count--) 449 { 450 r[i]=*color_data++; 451 g[i]=*color_data++; 452 b[i]=*color_data++; 453 i++; 454 } 455 head1_palette(r,g,b); 456 // if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b); 457 } 458 459 /* Put the display into one of the Display Power Management modes. */ 460 status_t SET_DPMS_MODE(uint32 dpms_flags) { 461 interrupt_enable(false); 462 463 LOG(4,("SET_DPMS_MODE: 0x%08x\n", dpms_flags)); 464 465 #if 0 466 if (si->dm.flags & DUALHEAD_BITS) /*dualhead*/ 467 { 468 switch(dpms_flags) 469 { 470 case B_DPMS_ON: /* H: on, V: on, display on */ 471 head1_dpms(true, true, true); 472 if (si->ps.secondary_head) head2_dpms(true, true, true); 473 break; 474 case B_DPMS_STAND_BY: 475 head1_dpms(false, false, true); 476 if (si->ps.secondary_head) head2_dpms(false, false, true); 477 break; 478 case B_DPMS_SUSPEND: 479 head1_dpms(false, true, false); 480 if (si->ps.secondary_head) head2_dpms(false, true, false); 481 break; 482 case B_DPMS_OFF: /* H: off, V: off, display off */ 483 head1_dpms(false, false, false); 484 if (si->ps.secondary_head) head2_dpms(false, false, false); 485 break; 486 default: 487 LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags)); 488 interrupt_enable(true); 489 return B_ERROR; 490 } 491 } else /* singlehead */ 492 #endif 493 { 494 switch(dpms_flags) 495 { 496 case B_DPMS_ON: /* H: on, V: on, display on */ 497 head1_dpms(true, true, true); 498 break; 499 case B_DPMS_STAND_BY: 500 head1_dpms(false, false, true); 501 break; 502 case B_DPMS_SUSPEND: 503 head1_dpms(false, true, false); 504 break; 505 case B_DPMS_OFF: /* H: off, V: off, display off */ 506 head1_dpms(false, false, false); 507 break; 508 default: 509 LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags)); 510 interrupt_enable(true); 511 return B_ERROR; 512 } 513 } 514 interrupt_enable(true); 515 return B_OK; 516 } 517 518 /* Report device DPMS capabilities */ 519 uint32 DPMS_CAPABILITIES(void) { 520 return (B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF); 521 } 522 523 /* Return the current DPMS mode */ 524 uint32 DPMS_MODE(void) { 525 bool display, h, v; 526 527 interrupt_enable(false); 528 head1_dpms_fetch(&display, &h, &v); 529 530 interrupt_enable(true); 531 532 if (display && h && v) 533 return B_DPMS_ON; 534 else if(v) 535 return B_DPMS_STAND_BY; 536 else if(h) 537 return B_DPMS_SUSPEND; 538 else 539 return B_DPMS_OFF; 540 } 541