/* 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-12/2015 */ #define MODULE_BIT 0x00200000 #include "acc_std.h" /* 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; uint32 startadd,startadd_right; // 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 %ukHz\n", target.timing.pixel_clock)); LOG(1, ("SETMODE: requested virtual_width %u, virtual_height %u\n", target.virtual_width, target.virtual_height)); /* See BOUNDS WARNING above... */ if (PROPOSE_DISPLAY_MODE(&target, &target, &target) == B_ERROR) return B_ERROR; /* make sure a possible 3D add-on will block rendering and re-initialize itself. * note: update in _this_ order only */ /* SET_DISPLAY_MODE will reset this flag when it's done. */ si->engine.threeD.mode_changing = true; /* every 3D add-on will reset this bit-flag when it's done. */ si->engine.threeD.newmode = 0xffffffff; /* every 3D clone needs to reclaim a slot. * note: this also cleans up reserved channels for killed 3D clones.. */ si->engine.threeD.clones = 0x00000000; /* disable interrupts using the kernel driver */ head1_interrupt_enable(false); if (si->ps.secondary_head) head2_interrupt_enable(false); /* disable TVout if supported */ if (si->ps.tvout) BT_stop_tvout(); /* turn off screen(s) _after_ TVout is disabled (if applicable) */ head1_dpms(false, false, false, true); if (si->ps.secondary_head) head2_dpms(false, false, false, true); if (si->ps.tvout) BT_dpms(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 */ nv_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 */ //fixme: remove or block on autodetect fail. (is now shutoff) if ((0) && (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 = nv_dac_crt_connected(); // crt2 = nv_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 */ nv_general_head_select(true); else nv_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")); 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 */ //fixme: we don't support interlaced mode? si->interlaced_tv_mode = false; /*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); head2_set_timing(target2); /* TVout support: program TVout encoder and modify CRTC timing */ if (si->ps.tvout && (target2.flags & TV_BITS)) BT_setmode(target2); } else /* single head mode */ { 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 = nv_dac_crt_connected(); // crt2 = nv_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 */ nv_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 */ if (head1_set_pix_pll(target) == 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); /* TVout support: program TVout encoder and modify CRTC timing */ if (si->ps.tvout && (target.flags & TV_BITS)) BT_setmode(target); //fixme: shut-off the videoPLL if it exists... } /* update driver's mode store */ si->dm = target; /* update FIFO data fetching according to mode */ nv_crtc_update_fifo(); if (si->ps.secondary_head) nv_crtc2_update_fifo(); /* set up acceleration for this mode */ /* note: * Maybe later we can forget about non-DMA mode (depends on 3D acceleration * attempts). */ if (!si->settings.block_acc) { if (!si->settings.dma_acc) nv_acc_init(); else nv_acc_init_dma(); } /* set up overlay unit for this mode */ nv_bes_init(); /* note freemem range */ /* first free adress follows hardcursor and workspace */ si->engine.threeD.mem_low = si->fbc.bytes_per_row * si->dm.virtual_height; if (si->settings.hardcursor) si->engine.threeD.mem_low += 2048; /* last free adress is end-of-ram minus max space needed for overlay bitmaps */ //fixme possible: //if overlay buffers are allocated subtract buffersize from mem_high; //only allocate overlay buffers if 3D is not in use. (block overlay during 3D) si->engine.threeD.mem_high = si->ps.memory_size - 1; /* Keep some extra distance as a workaround for certain bugs (see * DriverInterface.h for an explanation). */ if (si->ps.card_arch < NV40A) si->engine.threeD.mem_high -= PRE_NV40_OFFSET; else si->engine.threeD.mem_high -= NV40_PLUS_OFFSET; si->engine.threeD.mem_high -= (MAXBUFFERS * 1024 * 1024 * 2); /* see overlay.c file */ /* restore screen(s) output state(s) */ SET_DPMS_MODE(si->dpms_flags); /* enable interrupts using the kernel driver */ //fixme: //add head2 once we use one driver instance 'per head' (instead of 'per card') head1_interrupt_enable(true); /* make sure a possible 3D add-on will re-initialize itself by signalling ready */ si->engine.threeD.mode_changing = false; /* optimize memory-access if needed */ // head1_mem_priority(colour_depth1); /* Tune RAM CAS-latency if needed. Must be done *here*! */ nv_set_cas_latency(); LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0)); return B_OK; } /* Set which pixel of the virtual frame buffer will show up in the top left corner of the display device. Used for page-flipping games 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); /* disable interrupts using the kernel driver */ head1_interrupt_enable(false); if (si->ps.secondary_head) head2_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; } //fixme: //add head2 once we use one driver instance 'per head' (instead of 'per card') head1_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) { bool display, h1h, h1v, h2h, h2v, do_p1, do_p2; /* disable interrupts using the kernel driver */ head1_interrupt_enable(false); if (si->ps.secondary_head) head2_interrupt_enable(false); LOG(4,("SET_DPMS_MODE: $%08x\n", dpms_flags)); /* note current DPMS state for our reference */ si->dpms_flags = dpms_flags; /* preset: DPMS for panels should be executed */ do_p1 = do_p2 = true; /* determine signals to send to head(s) */ display = h1h = h1v = h2h = h2v = true; switch(dpms_flags) { case B_DPMS_ON: /* H: on, V: on, display on */ break; case B_DPMS_STAND_BY: display = h1h = h2h = false; break; case B_DPMS_SUSPEND: display = h1v = h2v = false; break; case B_DPMS_OFF: /* H: off, V: off, display off */ display = h1h = h1v = h2h = h2v = false; break; default: LOG(8,("SET: Invalid DPMS settings $%08x\n", dpms_flags)); //fixme: //add head2 once we use one driver instance 'per head' (instead of 'per card') head1_interrupt_enable(true); return B_ERROR; } /* CRTC used for TVout needs specific DPMS programming */ if (si->dm.flags & TV_BITS) { /* TV_PRIMARY tells us that the head to be used with TVout is the head that's * actually assigned as being the primary head at powerup: * so non dualhead-mode-dependant, and not 'fixed' CRTC1! */ if (si->dm.flags & TV_PRIMARY) { LOG(4,("SET_DPMS_MODE: tuning primary head DPMS settings for TVout compatibility\n")); if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) { if (!(si->settings.vga_on_tv)) { /* block VGA output on head displaying on TV */ /* Note: * this specific sync setting is required: Vsync is used to keep TVout * synchronized to the CRTC 'vertically' (otherwise 'rolling' occurs). * This leaves Hsync only for shutting off the VGA screen. */ h1h = false; h1v = true; /* block panel DPMS updates */ do_p1 = false; } else { /* when concurrent VGA is used alongside TVout on a head, DPMS is safest * applied this way: Vsync is needed for stopping TVout successfully when * a (new) modeswitch occurs. * (see routine BT_stop_tvout() in nv_brooktreetv.c) */ /* Note: * applying 'normal' DPMS here and forcing Vsync on in the above mentioned * routine seems to not always be enough: sometimes image generation will * not resume in that case. */ h1h = display; h1v = true; } } else { if (!(si->settings.vga_on_tv)) { h2h = false; h2v = true; do_p2 = false; } else { h2h = display; h2v = true; } } } else { LOG(4,("SET_DPMS_MODE: tuning secondary head DPMS settings for TVout compatibility\n")); if ((si->dm.flags & DUALHEAD_BITS) != DUALHEAD_SWITCH) { if (!(si->settings.vga_on_tv)) { h2h = false; h2v = true; do_p2 = false; } else { h2h = display; h2v = true; } } else { if (!(si->settings.vga_on_tv)) { h1h = false; h1v = true; do_p1 = false; } else { h1h = display; h1v = true; } } } } /* issue actual DPMS commands as far as applicable */ head1_dpms(display, h1h, h1v, do_p1); if ((si->ps.secondary_head) && (si->dm.flags & DUALHEAD_BITS)) head2_dpms(display, h2h, h2v, do_p2); if (si->dm.flags & TV_BITS) BT_dpms(display); //fixme: //add head2 once we use one driver instance 'per head' (instead of 'per card') head1_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) { return si->dpms_flags; }