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-5/2006
*/
#define MODULE_BIT 0x00200000
#include "acc_std.h"
Enable/Disable interrupts. Just a wrapper around the
ioctl() to the kernel driver.
*/
static void interrupt_enable(bool flag)
{
status_t result;
gx00_set_bool_state sbs;
if (si->ps.int_assigned)
{
sbs.magic = GX00_PRIVATE_DATA_MAGIC;
sbs.do_it = flag;
result = ioctl(fd, GX00_RUN_INTERRUPTS, &sbs, sizeof(sbs));
}
}
status_t SET_DISPLAY_MODE(display_mode *mode_to_set)
{
* 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 target;
uint8 colour_depth1 = 32;
status_t result;
uint32 startadd,startadd_right;
target = *mode_to_set;
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));
if (PROPOSE_DISPLAY_MODE(&target, &target, &target) == B_ERROR) return B_ERROR;
* in singlehead mode */
si->switched_crtcs = false;
interrupt_enable(false);
gx00_crtc_dpms(false, false, false);
if (si->ps.secondary_head)
{
g400_crtc2_dpms(false, false, false);
}
else
{
if (si->ps.tvout) gx00_maven_dpms(false, false, false);
}
startadd = (uint8*)si->fbc.frame_buffer - (uint8*)si->framebuffer;
gx00_general_validate_pic_size (&target, &si->fbc.bytes_per_row, &si->acc_mode);
if (target.flags & DUALHEAD_BITS)
{
uint8 colour_depth2 = colour_depth1;
display_mode target2 = target;
LOG(1,("SETMODE: setting DUALHEAD mode\n"));
LOG(8,("SETMODE: target clock %dkHz\n",target.timing.pixel_clock));
if (gx00_dac_set_pix_pll(target) == B_ERROR)
LOG(8,("SETMODE: error setting pixel clock (internal DAC)\n"));
if (!(target2.flags & TV_BITS))
{
LOG(8,("SETMODE: target2 clock %dkHz\n",target2.timing.pixel_clock));
if (gx00_maven_set_vid_pll(target2) == B_ERROR)
LOG(8,("SETMODE: error setting pixel clock (MAVEN)\n"));
}
switch(target.space)
{
case B_RGB16_LITTLE:
colour_depth1 = 16;
gx00_dac_mode(BPP16, 1.0);
gx00_crtc_depth(BPP16);
break;
case B_RGB32_LITTLE:
colour_depth1 = 32;
gx00_dac_mode(BPP32, 1.0);
gx00_crtc_depth(BPP32);
break;
}
switch(target2.space)
{
case B_RGB16_LITTLE:
colour_depth2 = 16;
gx00_maven_mode(BPP16, 1.0);
g400_crtc2_depth(BPP16);
break;
case B_RGB32_LITTLE:
colour_depth2 = 32;
gx00_maven_mode(BPP32DIR, 1.0);
g400_crtc2_depth(BPP32DIR);
break;
}
si->interlaced_tv_mode = false;
if ((target2.flags & TV_BITS) && (si->ps.card_type >= G450))
si->interlaced_tv_mode = true;
gx00_crtc_set_display_pitch ();
g400_crtc2_set_display_pitch ();
startadd_right=startadd+(target.timing.h_display * (colour_depth1 >> 3));
si->crtc_delay = 44;
if (colour_depth2 == 16) si->crtc_delay += 0;
switch (si->ps.card_type)
{
case G400:
case G400MAX:
* assuming connected straight through. */
target2.timing.v_display++;
switch (target.flags & DUALHEAD_BITS)
{
case DUALHEAD_ON:
case DUALHEAD_CLONE:
gx00_general_dac_select(DS_CRTC1DAC_CRTC2MAVEN);
si->switched_crtcs = false;
break;
case DUALHEAD_SWITCH:
if (i2c_sec_tv_adapter() == B_OK)
{
* and primary head output will be limited to 135Mhz pixelclock. */
LOG(4,("SETMODE: secondary TV-adapter detected, switching buffers\n"));
gx00_general_dac_select(DS_CRTC1DAC_CRTC2MAVEN);
si->switched_crtcs = true;
}
else
{
* but you can use overlay on the other output now. */
LOG(4,("SETMODE: no secondary TV-adapter detected, switching CRTCs\n"));
gx00_general_dac_select(DS_CRTC1MAVEN_CRTC2DAC);
si->switched_crtcs = false;
si->crtc_delay = 17;
if (colour_depth1 == 16) si->crtc_delay += 4;
* cross connected. */
target.timing.v_display++;
target2.timing.v_display--;
}
break;
}
break;
case G450:
case G550:
if (!si->ps.primary_dvi)
{
switch (target.flags & DUALHEAD_BITS)
{
case DUALHEAD_ON:
case DUALHEAD_CLONE:
gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
si->switched_crtcs = false;
break;
case DUALHEAD_SWITCH:
if (i2c_sec_tv_adapter() == B_OK)
{
* and primary head output will be limited to 235Mhz pixelclock. */
LOG(4,("SETMODE: secondary TV-adapter detected, switching buffers\n"));
gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
si->switched_crtcs = true;
}
else
{
* but you can use overlay on the other output now. */
LOG(4,("SETMODE: no secondary TV-adapter detected, switching CRTCs\n"));
gx00_general_dac_select(DS_CRTC1CON2_CRTC2CON1);
si->switched_crtcs = false;
}
break;
}
}
else
{
switch (target.flags & DUALHEAD_BITS)
{
case DUALHEAD_ON:
case DUALHEAD_CLONE:
if (i2c_sec_tv_adapter() == B_OK)
{
gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
si->switched_crtcs = false;
}
else
{
* but you can use overlay on the other output now. */
gx00_general_dac_select(DS_CRTC1CON2_CRTC2CON1);
si->switched_crtcs = false;
}
break;
case DUALHEAD_SWITCH:
if (i2c_sec_tv_adapter() == B_OK)
{
* and primary head output will be limited to 235Mhz pixelclock. */
LOG(4,("SETMODE: secondary TV-adapter detected, switching buffers\n"));
gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
si->switched_crtcs = true;
}
else
{
LOG(4,("SETMODE: no secondary TV-adapter detected, switching CRTCs\n"));
gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
si->switched_crtcs = false;
}
break;
}
}
break;
default:
break;
}
if (si->switched_crtcs)
{
uint32 temp = startadd;
startadd = startadd_right;
startadd_right = temp;
}
switch (target.flags & DUALHEAD_BITS)
{
case DUALHEAD_ON:
case DUALHEAD_SWITCH:
gx00_crtc_set_display_start(startadd,colour_depth1);
g400_crtc2_set_display_start(startadd_right,colour_depth2);
break;
case DUALHEAD_CLONE:
gx00_crtc_set_display_start(startadd,colour_depth1);
g400_crtc2_set_display_start(startadd,colour_depth2);
break;
}
gx00_crtc_set_timing(target);
if (!(target2.flags & TV_BITS)) result = g400_crtc2_set_timing(target2);
if (si->ps.tvout && (target2.flags & TV_BITS)) maventv_init(target2);
}
else
{
status_t status;
int colour_mode = BPP32;
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;
}
if (si->ps.card_type >= G100)
status = gx00_dac_set_pix_pll(target);
else
{
status = mil2_dac_set_pix_pll((target.timing.pixel_clock)/1000.0, colour_depth1);
}
if (status==B_ERROR)
LOG(8,("CRTC: error setting pixel clock (internal DAC)\n"));
gx00_dac_mode(colour_mode,1.0);
gx00_crtc_depth(colour_mode);
si->interlaced_tv_mode = false;
gx00_crtc_set_display_pitch();
gx00_crtc_set_display_start(startadd,colour_depth1);
switch (si->ps.card_type)
{
case G100:
case G200:
case G400:
case G400MAX:
if (!si->ps.secondary_head && si->ps.tvout && (target.flags & TV_BITS))
gx00_general_dac_select(DS_CRTC1MAVEN);
else
gx00_general_dac_select(DS_CRTC1DAC);
break;
case G450:
case G550:
gx00_general_dac_select(DS_CRTC1CON1_CRTC2CON2);
gx50_general_output_select();
break;
default:
break;
}
if (!si->ps.secondary_head && si->ps.tvout && (target.flags & TV_BITS))
{
si->crtc_delay = 17;
if (colour_depth1 == 16) si->crtc_delay += 4;
maventv_init(target);
}
else
{
gx00_maven_shutoff();
gx00_crtc_set_timing(target);
}
}
si->dm = target;
gx00_acc_init();
SET_DPMS_MODE(si->dpms_flags);
* MAVEN hardware design fault 'fix'.
* Note:
* Not applicable for singlehead cards with a MAVEN, since it's only used
* for TVout there. */
if ((target.flags & DUALHEAD_BITS) && (si->ps.card_type <= G400MAX))
gx00_maven_clrline();
LOG(1,("SETMODE: booted since %f mS\n", system_time()/1000.0));
interrupt_enable(true);
gx00_crtc_mem_priority(colour_depth1);
mga_set_cas_latency();
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));
G400 CRTC2 handles multiples of 32 for 16-bit and 16 for 32-bit - must stoop to this in dualhead
*/
if (si->dm.flags & DUALHEAD_BITS)
{
switch(si->dm.space)
{
case B_RGB16_LITTLE:
colour_depth=16;
h_display_start &= ~0x1f;
break;
case B_RGB32_LITTLE:
colour_depth=32;
h_display_start &= ~0x0f;
break;
default:
LOG(8,("SET:Invalid DH colour depth 0x%08x, should never happen\n", si->dm.space));
return B_ERROR;
}
}
else
{
switch(si->dm.space)
{
case B_CMAP8:
colour_depth=8;
h_display_start &= ~0x07;
break;
case B_RGB15_LITTLE: case B_RGB16_LITTLE:
colour_depth=16;
h_display_start &= ~0x03;
break;
case B_RGB32_LITTLE:
colour_depth=32;
h_display_start &= ~0x01;
break;
default:
return B_ERROR;
}
}
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;
si->dm.h_display_start = h_display_start;
si->dm.v_display_start = v_display_start;
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);
if (si->switched_crtcs)
{
uint32 temp = startadd;
startadd = startadd_right;
startadd_right = temp;
}
interrupt_enable(false);
switch (si->dm.flags&DUALHEAD_BITS)
{
case DUALHEAD_ON:
case DUALHEAD_SWITCH:
gx00_crtc_set_display_start(startadd,colour_depth);
g400_crtc2_set_display_start(startadd_right,colour_depth);
break;
case DUALHEAD_OFF:
gx00_crtc_set_display_start(startadd,colour_depth);
break;
case DUALHEAD_CLONE:
gx00_crtc_set_display_start(startadd,colour_depth);
g400_crtc2_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;
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++;
}
gx00_dac_palette(r,g,b);
}
status_t SET_DPMS_MODE(uint32 dpms_flags)
{
interrupt_enable(false);
LOG(4,("SET_DPMS_MODE: 0x%08x\n", dpms_flags));
si->dpms_flags = dpms_flags;
if (si->dm.flags & DUALHEAD_BITS)
{
switch(dpms_flags)
{
case B_DPMS_ON:
gx00_crtc_dpms(true, true, true);
if (si->ps.secondary_head) g400_crtc2_dpms(true, true, true);
break;
case B_DPMS_STAND_BY:
if (si->settings.greensync)
{
gx00_crtc_dpms(false, true, true);
}
else
{
gx00_crtc_dpms(false, false, true);
}
if (si->ps.secondary_head)
{
if ((si->dm.flags & TV_BITS) && (si->ps.card_type > G400MAX))
{
g400_crtc2_dpms(true, false, true);
}
else
{
g400_crtc2_dpms(false, false, true);
}
}
break;
case B_DPMS_SUSPEND:
if (si->settings.greensync)
{
gx00_crtc_dpms(false, true, true);
}
else
{
gx00_crtc_dpms(false, true, false);
}
if (si->ps.secondary_head)
{
if ((si->dm.flags & TV_BITS) && (si->ps.card_type > G400MAX))
{
g400_crtc2_dpms(true, true, false);
}
else
{
g400_crtc2_dpms(false, true, false);
}
}
break;
case B_DPMS_OFF:
if (si->settings.greensync)
{
gx00_crtc_dpms(false, true, true);
}
else
{
gx00_crtc_dpms(false, false, false);
}
if (si->ps.secondary_head)
{
if ((si->dm.flags & TV_BITS) && (si->ps.card_type > G400MAX))
{
g400_crtc2_dpms(true, false, false);
}
else
{
g400_crtc2_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
{
switch(dpms_flags)
{
case B_DPMS_ON:
gx00_crtc_dpms(true, true, true);
if (si->dm.flags & TV_BITS) gx00_maven_dpms(true, true, true);
break;
case B_DPMS_STAND_BY:
if (si->settings.greensync)
{
gx00_crtc_dpms(false, true, true);
}
else
{
gx00_crtc_dpms(false, false, true);
}
if (si->dm.flags & TV_BITS) gx00_maven_dpms(false, false, true);
break;
case B_DPMS_SUSPEND:
if (si->settings.greensync)
{
gx00_crtc_dpms(false, true, true);
}
else
{
gx00_crtc_dpms(false, true, false);
}
if (si->dm.flags & TV_BITS) gx00_maven_dpms(false, true, false);
break;
case B_DPMS_OFF:
if (si->settings.greensync)
{
gx00_crtc_dpms(false, true, true);
}
else
{
gx00_crtc_dpms(false, false, false);
}
if (si->dm.flags & TV_BITS) gx00_maven_dpms(false, false, false);
break;
default:
LOG(8,("SET: Invalid DPMS settings (SH) 0x%08x\n", dpms_flags));
interrupt_enable(true);
return B_ERROR;
}
}
interrupt_enable(true);
return B_OK;
}
uint32 DPMS_CAPABILITIES(void)
{
if (si->settings.greensync)
* modes anyway. */
return B_DPMS_ON | B_DPMS_OFF;
else
return B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF;
}
uint32 DPMS_MODE(void)
{
return si->dpms_flags;
}