/*Copyright 1999, Be Incorporated. All Rights Reserved.This file may be used under the terms of the Be Sample Code License.Other authors:Mark Watson,Apsed,Rudolf Cornelissen 11/2002-4/2004*/#define MODULE_BIT 0x00200000#include "acc_std.h"/*Enable/Disable interrupts. Just a wrapper around theioctl() to the kernel driver.*/static void interrupt_enable(bool flag) {status_t result;eng_set_bool_state sbs;/* set the magic number so the driver knows we're for real */sbs.magic = SKEL_PRIVATE_DATA_MAGIC;sbs.do_it = flag;/* contact driver and get a pointer to the registers and shared data */result = ioctl(fd, ENG_RUN_INTERRUPTS, &sbs, sizeof(sbs));}/* First validate the mode, then call lots of bit banging stuff to set the mode(s)! */status_t SET_DISPLAY_MODE(display_mode *mode_to_set){/* BOUNDS WARNING:* It's impossible to deviate whatever small amount in a display_mode if the lower* and upper limits are the same!* Besides:* BeOS (tested R5.0.3PE) is failing BWindowScreen::SetFrameBuffer() if PROPOSEMODE* returns B_BAD_VALUE!* Which means PROPOSEMODE should not return that on anything except on* deviations for:* display_mode.virtual_width;* display_mode.virtual_height;* display_mode.timing.h_display;* display_mode.timing.v_display;* So:* We don't use bounds here by making sure bounds and target are the same struct!* (See the call to PROPOSE_DISPLAY_MODE below) */display_mode /*bounds,*/ target;uint8 colour_depth1 = 32;// status_t result;uint32 startadd,startadd_right;// bool display, h, v;// bool crt1, crt2, cross;/* Adjust mode to valid one and fail if invalid */target /*= bounds*/ = *mode_to_set;/* show the mode bits */LOG(1, ("SETMODE: (ENTER) initial modeflags: $%08x\n", target.flags));LOG(1, ("SETMODE: requested target pixelclock %dkHz\n", target.timing.pixel_clock));LOG(1, ("SETMODE: requested virtual_width %d, virtual_height %d\n",target.virtual_width, target.virtual_height));/* See BOUNDS WARNING above... */if (PROPOSE_DISPLAY_MODE(&target, &target, &target) == B_ERROR) return B_ERROR;/* if not dualhead capable card clear dualhead flags */if (!(target.flags & DUALHEAD_CAPABLE)){target.flags &= ~DUALHEAD_BITS;}/* if not TVout capable card clear TVout flags */if (!(target.flags & TV_CAPABLE)){target.flags &= ~TV_BITS;}LOG(1, ("SETMODE: (CONT.) validated command modeflags: $%08x\n", target.flags));/* disable interrupts using the kernel driver */interrupt_enable(false);/* find current DPMS state, then turn off screen(s) */// head1_dpms_fetch(&display, &h, &v);// head1_dpms(false, false, false);// if (si->ps.secondary_head) head2_dpms(false, false, false);/*where in framebuffer the screen is (should this be dependant on previous MOVEDISPLAY?)*/startadd = (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;/* calculate and set new mode bytes_per_row */eng_general_validate_pic_size (&target, &si->fbc.bytes_per_row, &si->acc_mode);/*Perform the very long mode switch!*/if (target.flags & DUALHEAD_BITS) /*if some dualhead mode*/{uint8 colour_depth2 = colour_depth1;/* init display mode for secondary head */display_mode target2 = target;LOG(1,("SETMODE: setting DUALHEAD mode\n"));/* validate flags for secondary TVout */// if ((i2c_sec_tv_adapter() != B_OK) && (target2.flags & TV_BITS))// {// target.flags &= ~TV_BITS;//still needed for some routines...// target2.flags &= ~TV_BITS;// LOG(1,("SETMODE: blocking TVout: no TVout cable connected!\n"));// }/* detect which connectors have a CRT connected *///fixme: 'hot-plugging' for analog monitors removed: remove code as well;//or make it work with digital panels connected as well.// crt1 = eng_dac_crt_connected();// crt2 = eng_dac2_crt_connected();/* connect outputs 'straight-through' */// if (crt1)// {/* connector1 is used as primary output */// cross = false;// }// else// {// if (crt2)/* connector2 is used as primary output */// cross = true;// else/* no CRT detected: assume connector1 is used as primary output */// cross = false;// }/* set output connectors assignment if possible */// if ((target.flags & DUALHEAD_BITS) == DUALHEAD_SWITCH)/* invert output assignment in switch mode */// eng_general_head_select(true);// else// eng_general_head_select(false);/* set the pixel clock PLL(s) */LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock));// if (head1_set_pix_pll(target) == B_ERROR)// LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n"));/* we do not need to set the pixelclock here for a head that's in TVout mode */// if (!(target2.flags & TV_BITS))// {// LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock));// if (head2_set_pix_pll(target2) == B_ERROR)// LOG(8,("SETMODE: error setting pixel clock (DAC2)\n"));// }/*set the colour depth for CRTC1 and the DAC */switch(target.space){case B_CMAP8:colour_depth1 = 8;// head1_mode(BPP8, 1.0);// head1_depth(BPP8);break;case B_RGB15_LITTLE:colour_depth1 = 16;// head1_mode(BPP15, 1.0);// head1_depth(BPP15);break;case B_RGB16_LITTLE:colour_depth1 = 16;// head1_mode(BPP16, 1.0);// head1_depth(BPP16);break;case B_RGB32_LITTLE:colour_depth1 = 32;// head1_mode(BPP32, 1.0);// head1_depth(BPP32);break;}/*set the colour depth for CRTC2 and DAC2 */switch(target2.space){case B_CMAP8:colour_depth2 = 8;// head2_mode(BPP8, 1.0);// head2_depth(BPP8);break;case B_RGB15_LITTLE:colour_depth2 = 16;// head2_mode(BPP15, 1.0);// head2_depth(BPP15);break;case B_RGB16_LITTLE:colour_depth2 = 16;// head2_mode(BPP16, 1.0);// head2_depth(BPP16);break;case B_RGB32_LITTLE:colour_depth2 = 32;// head2_mode(BPP32, 1.0);// head2_depth(BPP32);break;}/* check if we are doing interlaced TVout mode */si->interlaced_tv_mode = false;/* if ((target2.flags & TV_BITS) && (si->ps.card_type >= G450))si->interlaced_tv_mode = true;*//*set the display(s) pitches*/// head1_set_display_pitch ();//fixme: seperate for real dualhead modes://we need a secondary si->fbc!// head2_set_display_pitch ();/*work out where the "right" screen starts*/startadd_right = startadd + (target.timing.h_display * (colour_depth1 >> 3));/* Tell card what memory to display */switch (target.flags & DUALHEAD_BITS){case DUALHEAD_ON:case DUALHEAD_SWITCH:// head1_set_display_start(startadd,colour_depth1);// head2_set_display_start(startadd_right,colour_depth2);break;case DUALHEAD_CLONE:// head1_set_display_start(startadd,colour_depth1);// head2_set_display_start(startadd,colour_depth2);break;}/* set the timing */// head1_set_timing(target);/* we do not need to setup CRTC2 here for a head that's in TVout mode */// if (!(target2.flags & TV_BITS)) result = head2_set_timing(target2);/* TVout support: setup CRTC2 and it's pixelclock */// if (si->ps.tvout && (target2.flags & TV_BITS)) maventv_init(target2);}else /* single head mode */{status_t status;int colour_mode = BPP32;/* connect output */if (si->ps.secondary_head){/* detect which connectors have a CRT connected *///fixme: 'hot-plugging' for analog monitors removed: remove code as well;//or make it work with digital panels connected as well.// crt1 = eng_dac_crt_connected();// crt2 = eng_dac2_crt_connected();/* connect outputs 'straight-through' */// if (crt1)// {/* connector1 is used as primary output */// cross = false;// }// else// {// if (crt2)/* connector2 is used as primary output */// cross = true;// else/* no CRT detected: assume connector1 is used as primary output */// cross = false;// }/* set output connectors assignment if possible */eng_general_head_select(false);}switch(target.space){case B_CMAP8: colour_depth1 = 8; colour_mode = BPP8; break;case B_RGB15_LITTLE: colour_depth1 = 16; colour_mode = BPP15; break;case B_RGB16_LITTLE: colour_depth1 = 16; colour_mode = BPP16; break;case B_RGB32_LITTLE: colour_depth1 = 32; colour_mode = BPP32; break;default:LOG(8,("SETMODE: Invalid singlehead colour depth 0x%08x\n", target.space));return B_ERROR;}/* set the pixel clock PLL */// status = head1_set_pix_pll(target);status = B_OK;if (status==B_ERROR)LOG(8,("CRTC: error setting pixel clock (internal DAC)\n"));/* set the colour depth for CRTC1 and the DAC *//* first set the colordepth */// head1_depth(colour_mode);/* then(!) program the PAL (<8bit colordepth does not support 8bit PAL) */// head1_mode(colour_mode,1.0);/* set the display pitch */// head1_set_display_pitch();/* tell the card what memory to display */// head1_set_display_start(startadd,colour_depth1);/* set the timing */// head1_set_timing(target);//fixme: shut-off the videoPLL if it exists...}/* update driver's mode store */si->dm = target;/* turn screen one on */// head1_dpms(display, h, v);/* turn screen two on if a dualhead mode is active */// if (target.flags & DUALHEAD_BITS) head2_dpms(display,h,v);/* set up acceleration for this mode */// eng_acc_init();/* set up overlay unit for this mode */// eng_bes_init();LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0));/* enable interrupts using the kernel driver */interrupt_enable(true);/* optimize memory-access if needed */// head1_mem_priority(colour_depth1);/* Tune RAM CAS-latency if needed. Must be done *here*! */eng_set_cas_latency();return B_OK;}/*Set which pixel of the virtual frame buffer will show up in thetop left corner of the display device. Used for page-flippinggames and virtual desktops.*/status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start) {uint8 colour_depth;uint32 startadd,startadd_right;LOG(4,("MOVE_DISPLAY: h %d, v %d\n", h_display_start, v_display_start));/* nVidia cards support pixelprecise panning on both heads in all modes:* No stepping granularity needed! *//* determine bits used for the colordepth */switch(si->dm.space){case B_CMAP8:colour_depth=8;break;case B_RGB15_LITTLE:case B_RGB16_LITTLE:colour_depth=16;break;case B_RGB24_LITTLE:colour_depth=24;break;case B_RGB32_LITTLE:colour_depth=32;break;default:return B_ERROR;}/* do not run past end of display */switch (si->dm.flags & DUALHEAD_BITS){case DUALHEAD_ON:case DUALHEAD_SWITCH:if (((si->dm.timing.h_display * 2) + h_display_start) > si->dm.virtual_width)return B_ERROR;break;default:if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width)return B_ERROR;break;}if ((si->dm.timing.v_display + v_display_start) > si->dm.virtual_height)return B_ERROR;/* everybody remember where we parked... */si->dm.h_display_start = h_display_start;si->dm.v_display_start = v_display_start;/* actually set the registers *///fixme: seperate both heads: we need a secondary si->fbc!startadd = v_display_start * si->fbc.bytes_per_row;startadd += h_display_start * (colour_depth >> 3);startadd += (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;startadd_right = startadd + si->dm.timing.h_display * (colour_depth >> 3);interrupt_enable(false);switch (si->dm.flags & DUALHEAD_BITS){case DUALHEAD_ON:case DUALHEAD_SWITCH:// head1_set_display_start(startadd,colour_depth);// head2_set_display_start(startadd_right,colour_depth);break;case DUALHEAD_OFF:// head1_set_display_start(startadd,colour_depth);break;case DUALHEAD_CLONE:// head1_set_display_start(startadd,colour_depth);// head2_set_display_start(startadd,colour_depth);break;}interrupt_enable(true);return B_OK;}/* Set the indexed color palette */void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) {int i;uint8 *r,*g,*b;/* Protect gamma correction when not in CMAP8 */if (si->dm.space != B_CMAP8) return;r=si->color_data;g=r+256;b=g+256;i=first;while (count--){r[i]=*color_data++;g[i]=*color_data++;b[i]=*color_data++;i++;}// head1_palette(r,g,b);if (si->dm.flags & DUALHEAD_BITS) head2_palette(r,g,b);}/* Put the display into one of the Display Power Management modes. */status_t SET_DPMS_MODE(uint32 dpms_flags) {interrupt_enable(false);LOG(4,("SET_DPMS_MODE: 0x%08x\n", dpms_flags));if (si->dm.flags & DUALHEAD_BITS) /*dualhead*/{switch(dpms_flags){case B_DPMS_ON: /* H: on, V: on, display on */// head1_dpms(true, true, true);// if (si->ps.secondary_head) head2_dpms(true, true, true);break;case B_DPMS_STAND_BY:// head1_dpms(false, false, true);// if (si->ps.secondary_head) head2_dpms(false, false, true);break;case B_DPMS_SUSPEND:// head1_dpms(false, true, false);// if (si->ps.secondary_head) head2_dpms(false, true, false);break;case B_DPMS_OFF: /* H: off, V: off, display off */// head1_dpms(false, false, false);// if (si->ps.secondary_head) head2_dpms(false, false, false);break;default:LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags));interrupt_enable(true);return B_ERROR;}}else /* singlehead */{switch(dpms_flags){case B_DPMS_ON: /* H: on, V: on, display on */// head1_dpms(true, true, true);break;case B_DPMS_STAND_BY:// head1_dpms(false, false, true);break;case B_DPMS_SUSPEND:// head1_dpms(false, true, false);break;case B_DPMS_OFF: /* H: off, V: off, display off */// head1_dpms(false, false, false);break;default:LOG(8,("SET: Invalid DPMS settings (DH) 0x%08x\n", dpms_flags));interrupt_enable(true);return B_ERROR;}}interrupt_enable(true);return B_OK;}/* Report device DPMS capabilities */uint32 DPMS_CAPABILITIES(void) {return (B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF);}/* Return the current DPMS mode */uint32 DPMS_MODE(void) {bool display, h, v;interrupt_enable(false);// head1_dpms_fetch(&display, &h, &v);display = h = v = true;interrupt_enable(true);if (display && h && v)return B_DPMS_ON;else if(v)return B_DPMS_STAND_BY;else if(h)return B_DPMS_SUSPEND;elsereturn B_DPMS_OFF;}