Build system conversion and check in of ATI Mach64 driver for MYOB. Based on source written by Rene MacKinney.

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@8531 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Nathan Whitehorn
2004-08-02 20:37:59 +00:00
parent c87c9d9960
commit 80d8f99ee8
22 changed files with 5891 additions and 0 deletions
+2
View File
@@ -6,3 +6,5 @@ SubInclude OBOS_TOP src add-ons accelerants neomagic ;
SubInclude OBOS_TOP src add-ons accelerants nvidia ;
SubInclude OBOS_TOP src add-ons accelerants radeon ;
SubInclude OBOS_TOP src add-ons accelerants tdfx ;
SubIncludeGPL OBOS_TOP src add-ons accelerants atimach64 ;
@@ -0,0 +1,434 @@
#include <GraphicsDefs.h>
#include "GlobalData.h"
#include "generic.h"
#define STICKY_REGS 1
#define WRITE_REG(x, y) outw(x, y)
#define wait_for_slots(num) WaitQueue(num)
/*#define wait_for_slots(numSlots) \
{ \
uint32 FifoReg; \
\
do \
{ \
READ_REG(FIFO_STAT, FifoReg); \
} \
while ((FifoReg & 0xFFFF) >> (16 - numSlots)); \
}
*/
void SCREEN_TO_SCREEN_BLIT(engine_token *et, blit_params *list, uint32 count) {
uint32 Offset, Pitch;
uint32 BppEncoding;
uint32 XDir;
uint32 YDir;
uint32 src_top, src_left;
uint32 dest_top, dest_left;
uint32 width, height;
uint32 scissor_x = (si->dm.virtual_width - 1) << 16;
uint32 scissor_y = (si->dm.virtual_height - 1) << 16;
// Perform required calculations and do the blit.
// start of frame buffer
Offset = 1024;
Offset = Offset >> 3; // qword offset of the frame buffer in card memory; had
// better be qword-aligned.
Pitch = si->dm.virtual_width;
Pitch = (Pitch + 7) >> 3; // frame buffer stride in pixels*8
// encode pixel format
switch (si->dm.space & ~0x3000)
{
case B_CMAP8:
BppEncoding = 0x2;
break;
case B_RGB15_BIG:
case B_RGBA15_BIG:
case B_RGB15_LITTLE:
case B_RGBA15_LITTLE:
BppEncoding = 0x3;
break;
case B_RGB16_BIG:
case B_RGB16_LITTLE:
BppEncoding = 0x4;
break;
case B_RGB32_BIG:
case B_RGBA32_BIG:
case B_RGB32_LITTLE:
case B_RGBA32_LITTLE:
default:
BppEncoding = 0x6;
}
BppEncoding = (BppEncoding << 28) | (BppEncoding << 16) | (BppEncoding << 8)
| (BppEncoding << 4) | BppEncoding;
#if STICKY_REGS > 0
wait_for_slots(10);
WRITE_REG(SC_LEFT_RIGHT, scissor_x);
WRITE_REG(SC_TOP_BOTTOM, scissor_y);
WRITE_REG(DP_WRITE_MASK, 0xFFFFFFFF);
WRITE_REG(DP_PIX_WIDTH, BppEncoding);
WRITE_REG(DP_MIX, 0x00070007); // ROP = SRC
WRITE_REG(DP_SRC, 0x00000300);
WRITE_REG(CLR_CMP_CNTL, 0x0);
// Pitch and offset had better not be out of range.
WRITE_REG(SRC_OFF_PITCH, (Pitch << 22) | Offset);
WRITE_REG(DST_OFF_PITCH, (Pitch << 22) | Offset);
WRITE_REG(SRC_CNTL, 0x0);
/* update fifo count */
si->engine.count += 10;
#endif
/* program the blit */
while (count--) {
src_left = list->src_left;
src_top = list->src_top;
dest_left = list->dest_left;
dest_top = list->dest_top;
width = list->width + 1; /* + 1 because it seems to be non-inclusive */
height = list->height + 1;
// Adjust direction of blitting to allow for overlapping source and destination.
if (src_left < dest_left)
{
XDir = 0; // right to left
src_left += (width - 1);
dest_left += (width - 1);
}
else
XDir = 1; // left to right
if (src_top < dest_top)
{
YDir = 0; // bottom to top
src_top += (height - 1);
dest_top += (height - 1);
}
else
YDir = 1; // top to bottom
// Set up the rectangle blit
// Critital section - accessing card registers.
//lock_card();
#if STICKY_REGS > 0
wait_for_slots(5);
#else
wait_for_slots(15);
WRITE_REG(SC_LEFT_RIGHT, scissor_x);
WRITE_REG(SC_TOP_BOTTOM, scissor_y);
WRITE_REG(DP_WRITE_MASK, 0xFFFFFFFF);
WRITE_REG(DP_PIX_WIDTH, BppEncoding);
WRITE_REG(DP_MIX, 0x00070007); // ROP = SRC
WRITE_REG(DP_SRC, 0x00000300);
WRITE_REG(CLR_CMP_CNTL, 0x0);
// Pitch and offset had better not be out of range.
WRITE_REG(SRC_OFF_PITCH, (Pitch << 22) | Offset);
WRITE_REG(DST_OFF_PITCH, (Pitch << 22) | Offset);
WRITE_REG(SRC_CNTL, 0x0);
#endif
WRITE_REG(SRC_WIDTH1, width);
WRITE_REG(SRC_Y_X, ((uint32)src_left << 16) | src_top);
// Pitch and offset had better not be out of range.
WRITE_REG(DST_CNTL, (YDir << 1) | XDir);
WRITE_REG(DST_Y_X, ((uint32)dest_left << 16) | dest_top);
WRITE_REG(DST_HEIGHT_WIDTH, ((uint32)width << 16) | height); // This triggers drawing.
#if STICKY_REGS
/* update fifo count */
si->engine.count += 5;
#else
/* update fifo count */
si->engine.count += 15;
#endif
/* next one */
list++;
}
}
void hardware_rectangle(engine_token *et, uint32 colorIndex, fill_rect_params *list, uint32 count, uint32 ROP) {
uint32 Offset;
uint32 Pitch;
uint32 Y;
uint32 X;
uint32 Width;
uint32 Height;
uint32 Colour;
uint32 BppEncoding;
#if STICKY_REGS > 0
uint32 scissor_x = (si->dm.virtual_width - 1) << 16;
uint32 scissor_y = (si->dm.virtual_height - 1) << 16;
#endif
// Perform required calculations and do the blit.
// start of frame buffer
Offset = 1024;
Offset = Offset >> 3; // qword offset of the frame buffer in card memory; had
// better be qword-aligned.
Pitch = si->dm.virtual_width;
Pitch = (Pitch + 7) >> 3; // frame buffer stride in pixels*8
// Don't need this - rectangle is in frame buffer co-ordinates.
// X += (uint32) (CardInfo.Display.DisplayXPos);
// Y += (uint32) (CardInfo.Display.DisplayYPos);
// Fill colour dword with specified colour, though only ls should be necessary.
switch (si->dm.space & ~0x3000)
{
case B_CMAP8:
BppEncoding = 0x2;
colorIndex &= 0xFF;
Colour = colorIndex | (colorIndex << 8) | (colorIndex << 16) | (colorIndex << 24);
break;
case B_RGB15_BIG:
case B_RGBA15_BIG:
case B_RGB15_LITTLE:
case B_RGBA15_LITTLE:
BppEncoding = 0x3;
colorIndex &= 0xFFFF;
Colour = colorIndex | (colorIndex << 16);
break;
case B_RGB16_BIG:
case B_RGB16_LITTLE:
BppEncoding = 0x4;
colorIndex &= 0xFFFF;
Colour = colorIndex | (colorIndex << 16);
break;
case B_RGB32_BIG:
case B_RGBA32_BIG:
case B_RGB32_LITTLE:
case B_RGBA32_LITTLE:
default:
BppEncoding = 0x6;
Colour = colorIndex;
}
BppEncoding = (BppEncoding << 28) | (BppEncoding << 16) | (BppEncoding << 8) | (BppEncoding << 4) | BppEncoding;
#if STICKY_REGS > 0
wait_for_slots(11);
// This seems to be needed even though we aren't using a source trajectory.
WRITE_REG(SRC_CNTL, 0x0);
// Pitch and offset had better not be out of range.
WRITE_REG(DST_OFF_PITCH, (Pitch << 22) | Offset);
WRITE_REG(DP_FRGD_CLR, Colour);
WRITE_REG(DP_WRITE_MASK, 0xFFFFFFFF);
WRITE_REG(DP_PIX_WIDTH, BppEncoding);
WRITE_REG(DP_MIX, ROP);
WRITE_REG(DP_SRC, 0x00000100);
WRITE_REG(CLR_CMP_CNTL, 0x0);
WRITE_REG(GUI_TRAJ_CNTL, 0x3);
WRITE_REG(SC_LEFT_RIGHT, scissor_x);
WRITE_REG(SC_TOP_BOTTOM, scissor_y);
/* update fifo count */
si->engine.count += 11;
#endif
while (count--) {
X = list->left;
Y = list->top;
Width = list->right;
Width = Width - X + 1;
Height = list->bottom;
Height = Height - Y + 1;
#if STICKY_REGS > 0
wait_for_slots(2);
#else
wait_for_slots(13);
// This seems to be needed even though we aren't using a source trajectory.
WRITE_REG(SRC_CNTL, 0x0);
// Pitch and offset had better not be out of range.
WRITE_REG(DST_OFF_PITCH, (Pitch << 22) | Offset);
WRITE_REG(DP_FRGD_CLR, Colour);
WRITE_REG(DP_WRITE_MASK, 0xFFFFFFFF);
WRITE_REG(DP_PIX_WIDTH, BppEncoding);
WRITE_REG(DP_MIX, ROP);
WRITE_REG(DP_SRC, 0x00000100);
WRITE_REG(CLR_CMP_CNTL, 0x0);
WRITE_REG(GUI_TRAJ_CNTL, 0x3);
WRITE_REG(SC_LEFT_RIGHT, ((X + Width - 1) << 16) | X);
WRITE_REG(SC_TOP_BOTTOM, ((Y + Height - 1) << 16) | Y);
#endif
// X and Y should have been clipped to frame buffer, and so should be in range.
WRITE_REG(DST_Y_X, (X << 16) | Y);
WRITE_REG(DST_HEIGHT_WIDTH, (Width << 16) | Height); // This triggers drawing.
#if STICKY_REGS
/* update fifo count */
si->engine.count += 2;
#else
/* update fifo count */
si->engine.count += 13;
#endif
/* next rect */
list++;
}
}
void FILL_RECTANGLE(engine_token *et, uint32 colorIndex, fill_rect_params *list, uint32 count) {
hardware_rectangle(et, colorIndex, list, count, 0x00070007); // ROP = SRC
}
void INVERT_RECTANGLE(engine_token *et, fill_rect_params *list, uint32 count) {
hardware_rectangle(et, 0xFFFFFFFF, list, count, 0x00000000); // ROP = !DST
}
void FILL_SPAN(engine_token *et, uint32 colorIndex, uint16 *list, uint32 count) {
uint32 Offset;
uint32 Pitch;
uint32 Y;
uint32 X;
uint32 Width;
uint32 Height;
uint32 Colour;
uint32 BppEncoding;
#if STICKY_REGS > 0
uint32 scissor_x = (si->dm.virtual_width - 1) << 16;
uint32 scissor_y = (si->dm.virtual_height - 1) << 16;
#endif
// Perform required calculations and do the blit.
// start of frame buffer
Offset = 1024;
Offset = Offset >> 3; // qword offset of the frame buffer in card memory; had
// better be qword-aligned.
Pitch = si->dm.virtual_width;
Pitch = (Pitch + 7) >> 3; // frame buffer stride in pixels*8
// Fill colour dword with specified colour, though only ls should be necessary.
switch (si->dm.space & ~0x3000)
{
case B_CMAP8:
BppEncoding = 0x2;
colorIndex &= 0xFF;
Colour = colorIndex | (colorIndex << 8) | (colorIndex << 16) | (colorIndex << 24);
break;
case B_RGB15_BIG:
case B_RGBA15_BIG:
case B_RGB15_LITTLE:
case B_RGBA15_LITTLE:
BppEncoding = 0x3;
colorIndex &= 0xFFFF;
Colour = colorIndex | (colorIndex << 16);
break;
case B_RGB16_BIG:
case B_RGB16_LITTLE:
BppEncoding = 0x4;
colorIndex &= 0xFFFF;
Colour = colorIndex | (colorIndex << 16);
break;
case B_RGB32_BIG:
case B_RGBA32_BIG:
case B_RGB32_LITTLE:
case B_RGBA32_LITTLE:
default:
BppEncoding = 0x6;
Colour = colorIndex;
}
BppEncoding = (BppEncoding << 28) | (BppEncoding << 16) | (BppEncoding << 8) | (BppEncoding << 4) | BppEncoding;
#if STICKY_REGS > 0
wait_for_slots(11);
// This seems to be needed even though we aren't using a source trajectory.
WRITE_REG(SRC_CNTL, 0x0);
// Pitch and offset had better not be out of range.
WRITE_REG(DST_OFF_PITCH, (Pitch << 22) | Offset);
WRITE_REG(DP_FRGD_CLR, Colour);
WRITE_REG(DP_WRITE_MASK, 0xFFFFFFFF);
WRITE_REG(DP_PIX_WIDTH, BppEncoding);
WRITE_REG(DP_MIX, 0x00070007); // ROP = SRC
WRITE_REG(DP_SRC, 0x00000100);
WRITE_REG(CLR_CMP_CNTL, 0x0);
WRITE_REG(GUI_TRAJ_CNTL, 0x3);
WRITE_REG(SC_LEFT_RIGHT, scissor_x);
WRITE_REG(SC_TOP_BOTTOM, scissor_y);
/* update fifo count */
si->engine.count += 11;
#endif
/* span lines are always one pixel tall */
Height = 1;
while (count--) {
Y = (uint32)*list++;
X = (uint32)*list++;
Width = (uint32)*list++;
Width = Width - X + 1;
#if STICKY_REGS > 0
wait_for_slots(2);
#else
wait_for_slots(13);
// This seems to be needed even though we aren't using a source trajectory.
WRITE_REG(SRC_CNTL, 0x0);
// Pitch and offset had better not be out of range.
WRITE_REG(DST_OFF_PITCH, (Pitch << 22) | Offset);
WRITE_REG(DP_FRGD_CLR, Colour);
WRITE_REG(DP_WRITE_MASK, 0xFFFFFFFF);
WRITE_REG(DP_PIX_WIDTH, BppEncoding);
WRITE_REG(DP_MIX, 0x00070007); // ROP = SRC
WRITE_REG(DP_SRC, 0x00000100);
WRITE_REG(CLR_CMP_CNTL, 0x0);
WRITE_REG(GUI_TRAJ_CNTL, 0x3);
WRITE_REG(SC_LEFT_RIGHT, ((X + Width - 1) << 16) | X);
WRITE_REG(SC_TOP_BOTTOM, ((Y + Height - 1) << 16) | Y);
#endif
// X and Y should have been clipped to frame buffer, and so should be in range.
WRITE_REG(DST_Y_X, (X << 16) | Y);
WRITE_REG(DST_HEIGHT_WIDTH, (Width << 16) | Height); // This triggers drawing.
#if STICKY_REGS
/* update fifo count */
si->engine.count += 2;
#else
/* update fifo count */
si->engine.count += 13;
#endif
}
}
+210
View File
@@ -0,0 +1,210 @@
#include "GlobalData.h"
#include "generic.h"
void set_cursor_colors(void);
uint8 bit_mirror(uint8 bits);
uint16 bit_merger(uint8 andbits, uint8 xorbits);
void set_cursor_colors(void) {
/* a place-holder for a routine to set the cursor colors */
/* In our sample driver, it's only called by the INIT_ACCELERANT() */
/* Cursor color 0 is black */
outw(CUR_CLR0, 0x00000000);
/* Cursor color 1 is white */
outw(CUR_CLR1, 0xffffffff);
}
uint8
bit_mirror(uint8 bits)
{
int i;
uint8 res = 0;
for(i=0; i<8; i++) {
res |= (1 & (bits>>i)) <<(7-i);
}
return res;
}
uint16
bit_merger(uint8 andbits, uint8 xorbits)
{
int i;
uint16 res = 0;
uint8 bits = 0;
for(i=0; i<8; i++) {
bits = (((xorbits & (1<<i))>>i)<<1) | ((andbits & (1<<i))>>i);
switch (bits){
case 0: /* Transparent is 10 */
res |= (2 << (i*2));
break;
case 2: /* Inversion is 11 */
res |= (3 << (i*2));
break;
case 1: /* White is color 1 */
res |= (1 << (i*2));
break;
case 3: /* Black is color 0 */
break;
}
}
return res;
}
status_t SET_CURSOR_SHAPE(uint16 width, uint16 height, uint16 hot_x, uint16 hot_y, uint8 *andMask, uint8 *xorMask) {
int i, j, k;
uint8 andM, xorM;
uint16 *cdata = (uint16 *)si->cursor.data;
/* NOTE: Currently, for BeOS, cursor width and height must be equal to 16. */
if ((width != 16) || (height != 16))
{
return B_ERROR;
}
else if ((hot_x >= width) || (hot_y >= height))
{
return B_ERROR;
}
else
{
/* Turn off cursor */
SHOW_CURSOR(false);
/* Fill up data */
k = 0;
for(i = 0; i < height; i++) {
for(j = 0; j < (width / 8); j++) {
andM = bit_mirror(~*(andMask++));
xorM = bit_mirror(*(xorMask++));
/* convert 8 bits to 16 bits of mask */
cdata[j + (i*8)] =
bit_merger(andM, xorM);
}
}
/* Update cursor variables appropriately. */
si->cursor.width = width;
si->cursor.height = height;
si->cursor.hot_x = hot_x;
si->cursor.hot_y = hot_y;
/* Turn on cursor */
SHOW_CURSOR(true);
}
return B_OK;
}
/*
Move the cursor to the specified position on the desktop. If we're
using some kind of virtual desktop, adjust the display start position
accordingly and position the cursor in the proper "virtual" location.
*/
void
MOVE_CURSOR(uint16 x, uint16 y) {
bool move_screen = false;
uint16 hds = si->dm.h_display_start; /* the current horizontal starting pixel */
uint16 vds = si->dm.v_display_start; /* the current vertical starting line */
int xoff, yoff; /* offsets of cursor if it goes out of the screen */
/*
Most cards can't set the starting horizontal pixel to anything but a multiple
of eight. If your card can, then you can change the adjustment factor here.
Oftentimes the restriction is mode dependant, so you could be fancier and
perhaps get smoother horizontal scrolling with more work. It's a sample driver,
so we're going to be lazy here.
*/
uint16 h_adjust = 7; /* a mask to make horizontal values a multiple of 8 */
/* clamp cursor to virtual display */
if (x >= si->dm.virtual_width) x = si->dm.virtual_width - 1;
if (y >= si->dm.virtual_height) y = si->dm.virtual_height - 1;
/* adjust h/v_display_start to move cursor onto screen */
if (x >= (si->dm.timing.h_display + hds)) {
hds = ((x - si->dm.timing.h_display) + 1 + h_adjust) & ~h_adjust;
move_screen = true;
} else if (x < hds) {
hds = x & ~h_adjust;
move_screen = true;
}
if (y >= (si->dm.timing.v_display + vds)) {
vds = y - si->dm.timing.v_display + 1;
move_screen = true;
} else if (y < vds) {
vds = y;
move_screen = true;
}
/* reposition the desktop on the display if required */
if (move_screen) MOVE_DISPLAY(hds,vds);
/* put cursor in correct physical position */
x -= hds;
y -= vds;
/* Take care of hot_x hot_y */
xoff = x - si->cursor.hot_x;
yoff = y - si->cursor.hot_y;
/* Do we have to fake the cursor going out of the screen ? */
if (xoff < 0) {
xoff = -xoff;
} else {
xoff = 0;
x -= si->cursor.hot_x;
}
if (yoff < 0) {
yoff = -yoff;
} else {
yoff = 0;
y -= si->cursor.hot_y;
}
/* Position the offset of the cursor */
outw(CUR_HORZ_VERT_OFF, (yoff << 16) | xoff);
/* position the cursor on the display */
/* this will be card dependant */
/* printf(" Place %08x %d %d \n", si->regs + CUR_HORZ_VERT_POSN, x, y);*/
outw(CUR_HORZ_VERT_POSN, (y << 16) | x);
}
void SHOW_CURSOR(bool is_visible) {
if (is_visible) {
/* add cursor showing code here */
WaitQueue(4);
while (!(inw(CRTC_VLINE_CRNT_VLINE) & CRTC_CRNT_VLINE));
outb(GEN_TEST_CNTL, inb(GEN_TEST_CNTL) | HWCURSOR_ENABLE);
} else {
/* add cursor hiding code here */
WaitQueue(4);
outb(GEN_TEST_CNTL, inb(GEN_TEST_CNTL) & (~HWCURSOR_ENABLE));
}
WaitIdleEmpty();
/* record for our info */
si->cursor.is_visible = is_visible;
}
@@ -0,0 +1,83 @@
#include "GlobalData.h"
#include "generic.h"
static engine_token ati_engine_token = { 1, B_2D_ACCELERATION, NULL };
uint32 ACCELERANT_ENGINE_COUNT(void) {
return 1;
}
status_t ACQUIRE_ENGINE(uint32 capabilities, uint32 max_wait, sync_token *st, engine_token **et) {
/* acquire the shared benaphore */
AQUIRE_BEN(si->engine.lock)
/* sync if required */
if (st) SYNC_TO_TOKEN(st);
/* return an engine token */
*et = &ati_engine_token;
return B_OK;
}
status_t RELEASE_ENGINE(engine_token *et, sync_token *st) {
/* update the sync token, if any */
if (st) {
st->engine_id = et->engine_id;
st->counter = si->engine.count;
}
/* release the shared benaphore */
RELEASE_BEN(si->engine.lock)
return B_OK;
}
void WAIT_ENGINE_IDLE(void) {
// Wait until engine finishes
WaitIdleEmpty();
// note our current possition
si->engine.last_idle = si->engine.count;
}
status_t GET_SYNC_TOKEN(engine_token *et, sync_token *st) {
st->engine_id = et->engine_id;
st->counter = si->engine.count;
return B_OK;
}
status_t SYNC_TO_TOKEN(sync_token *st) {
uint64 fifo_diff;
uint64 fifo_limit;
/* a quick out */
if (st->counter < si->engine.last_idle) return B_OK;
/* the full monty */
fifo_limit = si->engine.fifo_limit;
/* fifo_mask = si->fifo_mask;*/
do {
/* calculate the age of the sync token */
fifo_diff = (vuint64)(si->engine.count) - st->counter;
/* add in the number of free slots in the fifo */
fifo_diff += (uint64)((inw(GUI_STAT) >> 16) & 0x003f);
/*
The astute observer will notice that the free slot counter
doesn't have enough bits to represent the full FIFO depth.
This means that for "recent" operations, we end up waiting
on engine idle :-(
*/
#if 1
/* add one if the engine is idle (for when st->counter == si->engine.count) */
if (!(inw(GUI_STAT) & 0x01)) fifo_diff++;
#endif
/* anything more than fifo_limit fifo slots ago is guaranteed done */
/* if the engine is idle, bail out */
} while ((fifo_diff <= fifo_limit) && (inw(GUI_STAT) & 0x01));
/* WAIT_ENGINE_IDLE(); */
si->engine.last_idle = st->counter;
return B_OK;
}
@@ -0,0 +1,86 @@
#include "generic.h"
/*
The standard entry point. Given a uint32 feature identifier, this routine
returns a pointer to the function that implements the feature. Some features
require more information than just the identifier to select the proper
function. The extra information (which is specific to the feature) is
pointed at by the void *data parameter. By default, no extra information
is available. Any extra information available to choose the function will be
noted on a case by case below.
*/
void * get_accelerant_hook(uint32 feature, void *data) {
switch (feature) {
/*
These definitions are out of pure lazyness.
*/
#define HOOK(x) case B_##x: return (void *)x
#define ZERO(x) case B_##x: return (void *)0
/*
One of either B_INIT_ACCELERANT or B_CLONE_ACCELERANT will be requested and
subsequently called before any other hook is requested. All other feature
hook selections can be predicated on variables assigned during the accelerant
initialization process.
*/
/* initialization */
HOOK(INIT_ACCELERANT);
HOOK(CLONE_ACCELERANT);
HOOK(ACCELERANT_CLONE_INFO_SIZE);
HOOK(GET_ACCELERANT_CLONE_INFO);
HOOK(UNINIT_ACCELERANT);
//HOOK(GET_ACCELERANT_DEVICE_INFO);
HOOK(ACCELERANT_RETRACE_SEMAPHORE);
/* mode configuration */
HOOK(ACCELERANT_MODE_COUNT);
HOOK(GET_MODE_LIST);
HOOK(PROPOSE_DISPLAY_MODE);
HOOK(SET_DISPLAY_MODE);
HOOK(GET_DISPLAY_MODE);
HOOK(GET_FRAME_BUFFER_CONFIG);
HOOK(GET_PIXEL_CLOCK_LIMITS);
HOOK(MOVE_DISPLAY);
HOOK(SET_INDEXED_COLORS);
//HOOK(GET_TIMING_CONSTRAINTS);
HOOK(DPMS_CAPABILITIES);
HOOK(DPMS_MODE);
HOOK(SET_DPMS_MODE);
/* cursor managment */
HOOK(SET_CURSOR_SHAPE);
HOOK(MOVE_CURSOR);
HOOK(SHOW_CURSOR);
/* synchronization */
HOOK(ACCELERANT_ENGINE_COUNT);
HOOK(ACQUIRE_ENGINE);
HOOK(RELEASE_ENGINE);
HOOK(WAIT_ENGINE_IDLE);
HOOK(GET_SYNC_TOKEN);
HOOK(SYNC_TO_TOKEN);
/*
When requesting an acceleration hook, the calling application provides a
pointer to the display_mode for which the acceleration function will be used.
Depending on the engine architecture, you may choose to provide a different
function to be used with each bit-depth. In the sample driver we return
the same function all the time.
*/
/* 2D acceleration */
HOOK(SCREEN_TO_SCREEN_BLIT);
HOOK(FILL_RECTANGLE);
HOOK(INVERT_RECTANGLE);
HOOK(FILL_SPAN);
#undef HOOK
#undef ZERO
}
/*
Return a null pointer for any feature we don't understand.
*/
return 0;
}
@@ -0,0 +1,73 @@
#include "GlobalData.h"
#include "generic.h"
extern uint32 calcBitsPerPixel(uint32 cs);
/*
Return the current display mode. The only time you might return an
error is if a mode hasn't been set.
*/
status_t GET_DISPLAY_MODE(display_mode *current_mode) {
/* easy for us, we return the last mode we set */
*current_mode = si->dm;
return B_OK;
}
/*
Return the frame buffer configuration information.
*/
status_t GET_FRAME_BUFFER_CONFIG(frame_buffer_config *afb) {
/* easy again, as the last mode set stored the info in a convienient form */
*afb = si->fbc;
return B_OK;
}
/*
Return the maximum and minium pixel clock limits for the specified mode.
*/
status_t GET_PIXEL_CLOCK_LIMITS(display_mode *dm, uint32 *low, uint32 *high) {
/*
Note that we're not making any guarantees about the ability of the attached
display to handle pixel clocks within the limits we return. A future monitor
capablilities database will post-process this information.
*/
uint32 total_pix = (uint32)dm->timing.h_total * (uint32)dm->timing.v_total;
uint32 clock_limit;
float i;
/* max pixel clock is pixel depth dependant */
switch (calcBitsPerPixel(dm->space)) {
case 32: clock_limit = si->pix_clk_max32; break;
case 15:
case 16: clock_limit = si->pix_clk_max16; break;
case 8: clock_limit = si->pix_clk_max8; break;
default:
clock_limit = 0;
}
/* printf("low %d, high %d %d \n", *low, clock_limit, si->pix_clk_max8);*/
/* lower limit of about 48Hz vertical refresh */
*low = (total_pix * 48L) / 1000L;
i = (total_pix * 48L) / 1000L;
/* printf("low %d, high %d %08X \n", *low, clock_limit, dm->space);*/
if (*low > clock_limit) return B_ERROR;
*high = clock_limit;
return B_OK;
}
/*
Return the semaphore id that will be used to signal a vertical retrace
occured.
*/
sem_id ACCELERANT_RETRACE_SEMAPHORE(void) {
/*
NOTE:
The kernel driver created this for us. We don't know if the system is
using real interrupts, or if we're faking it, and we don't care.
If we choose not to support this at all, we'd just return B_ERROR here,
and the user wouldn't get any kind of vertical retrace support.
*/
/* return si->vblank;*/
return B_ERROR;
}
@@ -0,0 +1,10 @@
#include "GlobalData.h"
int fd;
shared_info *si;
area_id shared_info_area;
vuint32 *regs;
area_id regs_area;
display_mode *atimach64_mode_list;
area_id atimach64_mode_list_area;
int accelerantIsClone;
@@ -0,0 +1,16 @@
#if !defined(GLOBALDATA_H)
#define GLOBALDATA_H
#include "DriverInterface.h"
#include "regmach64.h"
extern int fd;
extern shared_info *si;
extern area_id shared_info_area;
extern vuint32 *regs;
extern area_id regs_area;
extern display_mode *atimach64_mode_list;
extern area_id atimach64_mode_list_area;
extern int accelerantIsClone;
#endif
@@ -0,0 +1,372 @@
#include "GlobalData.h"
#include "generic.h"
#include "Mach64.h"
#include "string.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include <sys/ioctl.h>
#include "stdio.h"
/* defined in ProposeDisplayMode.c */
extern status_t create_mode_list(void);
/* defined in Cursor.c */
extern void set_cursor_colors(void);
// Determines the amount of card memory available by seeing how far up the frame buffer
// data can be written and read back reliably. Does a paranoia check to make sure that
// It isn't just wrapping, either.
unsigned long Get_Card_Mem_Size()
{
// Allowed sizes actually go up to 16 megs, but clip at the register window for now.
const unsigned long AllowedSizes[] =
{ 0x00080000, 0x00100000, 0x00180000, 0x00200000,
0x00280000, 0x00300000, 0x00380000, 0x00400000,
0x00500000, 0x00600000, 0x00700000, 0x007FF800,
0x0 };
unsigned long MaxMem;
unsigned long RWIndex;
int iMaxIndex, iTestIndex, iX;
unsigned long LTemp;
int IsOk;
uint32 *VramBase;
VramBase = (uint32 *)si->framebuffer;
MaxMem = 0; // Default.
IsOk = 1;
// Step through ever-larger memory sizes, recording size if passes test and
// ignoring otherwise.
for (iMaxIndex = 0; (AllowedSizes[iMaxIndex] != 0) && IsOk; iMaxIndex++)
{
// Write test values to the linear aperature.
// Only need to do this for the farthest location, as previous locations
// already have been written to in previous passes.
RWIndex = AllowedSizes[iMaxIndex];
RWIndex = (RWIndex - 16384) >> 2;
for (iX = 0; iX < 4096; iX++)
{
LTemp = RWIndex;
// Hash LTemp. As the parameters for the hash are prime, it should
// be extremely unlikely to get these values through a glitch, and
// the pattern only repeats at prime intervals, so aliasing shouldn't
// fool the test either.
LTemp = (263 * (LTemp % 65521) + 29) % 65521;
// Extend this to 32 bits.
LTemp |= (LTemp ^ 0x0000FFFFul) << 16;
VramBase[RWIndex] = LTemp;
RWIndex++;
}
// Verify that all test patterns are still intact. If values written past the
// end of memory drop off the face of the frame buffer, the farthest pattern(s)
// will not be what they should be. If values written past the end of memory
// wrap, then previous patterns will be overwritten (or partly overwritten,
// as the test location at 8 megs is actually at 8 megs - 2k).
// As soon as an invalid value is detected, IsOk is set to 0, which should
// quickly terminate the test loops.
for (iTestIndex = 0; (iTestIndex <= iMaxIndex) && IsOk; iTestIndex++)
{
RWIndex = AllowedSizes[iTestIndex];
RWIndex = (RWIndex - 16384) >> 2;
for (iX = 0; (iX < 4096) && IsOk; iX++)
{
LTemp = RWIndex;
// Hash LTemp. As the parameters for the hash are prime, it should
// be extremely unlikely to get these values through a glitch, and
// the pattern only repeats at prime intervals, so aliasing shouldn't
// fool the test either.
LTemp = (263 * (LTemp % 65521) + 29) % 65521;
// Extend this to 32 bits.
LTemp |= (LTemp ^ 0x0000FFFFul) << 16;
// Test against the value read from the frame buffer.
if (VramBase[RWIndex] != LTemp)
IsOk = 0;
RWIndex++;
}
}
// If the test patterns check out, update MaxMem accordingly.
if (IsOk)
MaxMem = AllowedSizes[iMaxIndex];
}
return MaxMem;
}
static status_t init_common(int the_fd);
/* Initialization code shared between primary and cloned accelerants */
static status_t init_common(int the_fd) {
status_t result;
atimach64_get_private_data gpd;
/* memorize the file descriptor */
fd = the_fd;
/* set the magic number so the driver knows we're for real */
gpd.magic = ATIMACH64_PRIVATE_DATA_MAGIC;
/* contact driver and get a pointer to the registers and shared data */
result = ioctl(fd, ATIMACH64_GET_PRIVATE_DATA, &gpd, sizeof(gpd));
if (result != B_OK) goto error0;
/* clone the shared area for our use */
shared_info_area = clone_area("ATIMACH64 shared info", (void **)&si, B_ANY_ADDRESS,
B_READ_AREA | B_WRITE_AREA, gpd.shared_info_area);
if (shared_info_area < 0) {
result = shared_info_area;
goto error0;
}
/* all done */
goto error0;
error0:
return result;
}
/* Clean up code shared between primary and cloned accelrants */
static void uninit_common(void) {
/* release the memory mapped registers */
/* delete_area(regs_area); */
/* a little cheap paranoia */
regs = 0;
/* release our copy of the shared info from the kernel driver */
delete_area(shared_info_area);
/* more cheap paranoia */
si = 0;
}
/*
Initialize the accelerant. the_fd is the file handle of the device (in
/dev/graphics) that has been opened by the app_server (or some test harness).
We need to determine if the kernel driver and the accelerant are compatible.
If they are, get the accelerant ready to handle other hook functions and
report success or failure.
*/
status_t INIT_ACCELERANT(int the_fd) {
status_t result;
/* note that we're the primary accelerant (accelerantIsClone is global) */
accelerantIsClone = 0;
/* do the initialization common to both the primary and the clones */
result = init_common(the_fd);
/* bail out if the common initialization failed */
if (result != B_OK) goto error0;
/*
If there is a possiblity that the kernel driver will recognize a card that
the accelerant can't support, you should check for that here. Perhaps some
odd memory configuration or some such.
*/
/*
This is a good place to go and initialize your card. The details are so
device specific, we're not even going to pretend to provide you with sample
code. If this fails, we'll have to bail out, cleaning up the resources
we've already allocated.
*/
/* call the device specific init code */
if (si->mem_size != Get_Card_Mem_Size()){
result = B_ERROR;
}
mach64InitAperture();
mach64ResetEngine();
WaitIdleEmpty();
/* Disable all interrupts */
outw(CRTC_INT_CNTL, 0);
WaitIdleEmpty();
/* bail out if it failed */
if (result != B_OK) goto error1;
/*
Now would be a good time to figure out what video modes your card supports.
We'll place the list of modes in another shared area so all of the copies
of the driver can see them. The primary copy of the accelerant (ie the one
initialized with this routine) will own the "one true copy" of the list.
Everybody else get's a read-only clone.
*/
result = create_mode_list();
if (result != B_OK) goto error2;
/*
Initialize the frame buffer and cursor pointers. Most newer video cards
have integrated the DAC into the graphics engine, and so the cursor shape
is stored in the frame buffer RAM. Also, newer cards tend not to have as
many restrictions about the placement of the start of the frame buffer in
frame buffer RAM. If you're supporting an older card with frame buffer
positioning restrictions, or one without an integrated DAC, you'll have to
change this accordingly.
*/
/*
Put the cursor at the start of the frame buffer. The typical 64x64 4 color
(black, white, transparent, inverse) takes up 1024 bytes of RAM.
*/
si->cursor.data = (uint8 *)si->framebuffer;
/* Initialize cursor as transparent */
memset(si->cursor.data, 0xaa, 1024);
/* Initialize the rest of the cursor information while we're here */
si->cursor.width = 0;
si->cursor.height = 0;
si->cursor.hot_x = 0;
si->cursor.hot_y = 0;
si->cursor.x = 0;
si->cursor.y = 0;
/* Tell the hardware all the cursor info */
outw(CUR_OFFSET, 0x00000000); /* Where it is in memory */
outw(CUR_HORZ_VERT_POSN, 0x00000000); /* Position in screen */
outw(CUR_HORZ_VERT_OFF, 0x00000000); /* Hot x,y */
/*
Put the frame buffer immediately following the cursor data. We store this
info in a frame_buffer_config structure to make it convienient to return
to the app_server later.
*/
si->fbc.frame_buffer = (void *)(((char *)si->framebuffer) + 1024);
si->fbc.frame_buffer_dma = (void *)(((char *)si->framebuffer_pci) + 1024);
/* init the shared semaphore */
INIT_BEN(si->engine.lock);
/* initialize the engine synchronization variables */
/* count of issued parameters or commands */
si->engine.last_idle = si->engine.count = 0;
/* bail out if something failed */
if (result != B_OK) goto error3;
/* set the cursor colors. You may or may not have to do this, depending
on the device. */
set_cursor_colors();
/* ensure cursor state */
SHOW_CURSOR(false);
/* a winner! */
result = B_OK;
goto error0;
error3:
/* free up the benaphore */
DELETE_BEN(si->engine.lock);
error2:
/*
Clean up any resources allocated in your device specific initialization
code.
*/
error1:
/*
Initialization failed after init_common() succeeded, so we need to clean
up before quiting.
*/
uninit_common();
error0:
return result;
}
/*
Return the number of bytes required to hold the information required
to clone the device.
*/
ssize_t ACCELERANT_CLONE_INFO_SIZE(void) {
/*
Since we're passing the name of the device as the only required
info, return the size of the name buffer
*/
return MAX_ATIMACH64_DEVICE_NAME_LENGTH;
}
/*
Return the info required to clone the device. void *data points to
a buffer at least ACCELERANT_CLONE_INFO_SIZE() bytes in length.
*/
void GET_ACCELERANT_CLONE_INFO(void *data) {
atimach64_device_name dn;
status_t result;
/* call the kernel driver to get the device name */
dn.magic = ATIMACH64_PRIVATE_DATA_MAGIC;
/* store the returned info directly into the passed buffer */
dn.name = (char *)data;
result = ioctl(fd, ATIMACH64_DEVICE_NAME, &dn, sizeof(dn));
}
/*
Initialize a copy of the accelerant as a clone. void *data points to
a copy of the data returned by GET_ACCELERANT_CLONE_INFO().
*/
status_t CLONE_ACCELERANT(void *data) {
status_t result;
char path[MAXPATHLEN];
/* the data is the device name */
strcpy(path, "/dev");
strcat(path, (const char *)data);
/* open the device, the permissions aren't important */
fd = open(path, B_READ_WRITE);
if (fd < 0) {
result = fd;
goto error0;
}
/* note that we're a clone accelerant */
accelerantIsClone = 1;
/* call the shared initialization code */
result = init_common(fd);
/* bail out if the common initialization failed */
if (result != B_OK) goto error1;
/* get shared area for display modes */
result = atimach64_mode_list_area = clone_area(
"ATIMACH64 cloned display_modes",
(void **)&atimach64_mode_list,
B_ANY_ADDRESS,
B_READ_AREA,
si->mode_area
);
if (result < B_OK) goto error2;
/* all done */
result = B_OK;
goto error0;
error2:
/* free up the areas we cloned */
uninit_common();
error1:
/* close the device we opened */
close(fd);
error0:
return result;
}
void UNINIT_ACCELERANT(void) {
/* free our mode list area */
delete_area(atimach64_mode_list_area);
/* paranoia */
atimach64_mode_list = 0;
/* release our cloned data */
uninit_common();
/* close the file handle ONLY if we're the clone */
if (accelerantIsClone) close(fd);
}
+21
View File
@@ -0,0 +1,21 @@
SubDir OBOS_TOP src add-ons accelerants atimach64 ;
UsePrivateHeaders graphics ;
UsePrivateHeaders [ FDirName graphics atimach64 ] ;
Addon atimach64.accelerant : accelerants :
Acceleration.c
Cursor.c
EngineManagment.c
GetAccelerantHook.c
GlobalData.c
GetModeInfo.c
InitAccelerant.c
Mach64.c
Mach64fifo.c
ProposeDisplayMode.c
SetDisplayMode.c
;
Depends atimach64.accelerant : atimach64.driver ;
+447
View File
@@ -0,0 +1,447 @@
/* All this code has been reversed engineered from Xfree86 Project :-)
Copyright 1999 by
Rene MacKinney <[email protected]>
7.May.99
*/
//------------------------------------------------------------------------
/* $XFree86: xc/programs/Xserver/hw/xfree86/accel/mach64/mach64.c,v 3.62.2.15 19
98/10/18 20:42:04 hohndel Exp $ */
/*
* Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
* Copyright 1993,1994,1995,1996,1997 by Kevin E. Martin, Chapel Hill, North Car
olina.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of Thomas Roell not be used in
* advertising or publicity pertaining to distribution of the software without
* specific, written prior permission. Thomas Roell makes no representations
* about the suitability of this software for any purpose. It is provided
* "as is" without express or implied warranty.
*
* THOMAS ROELL, KEVIN E. MARTIN, AND RICKARD E. FAITH DISCLAIM ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE AUTHORS
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
* DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: Thomas Roell, [email protected]
*
* Rewritten for the 8514/A by Kevin E. Martin ([email protected])
* Modified for the Mach-8 by Rickard E. Faith ([email protected])
* Rewritten for the Mach32 by Kevin E. Martin ([email protected])
* Rewritten for the Mach64 by Kevin E. Martin ([email protected])
* Support for the Mach64 CT added by David Dawes ([email protected])
*
*/
/* $XConsortium: mach64.c /main/34 1996/10/28 04:46:47 kaleb $ */
//---------------------------------------------------------------------------
#include <GraphicsDefs.h>
#include "GlobalData.h"
#include "generic.h"
#include "Mach64fifo.h"
#include "Mach64.h"
/*
* mach64FIFOdepth --
* Calculates the correct FIFO depth for the Mach64 depending on the
* color depth and clock selected.
*/
int mach64FIFOdepth(cdepth, clock, width)
int cdepth;
int clock;
int width;
{
int fifo_depth;
if (si->device_id == MACH64_VT_ID) {
if (si->revision == 0x48) { /* VTA4 */
fifo_depth = mach64FIFOdepthVTA4(cdepth, clock, width);
} else { /* VTA3 */
fifo_depth = mach64FIFOdepthVTA3(cdepth, clock, width);
}
} else if (si->device_id == MACH64_GT_ID) {
fifo_depth = mach64FIFOdepthGT(cdepth, clock, width);
} else if (si->device_id == MACH64_CT_ID && si->revision == 0x0a) {
/* CT-D has a larger FIFO and thus requires special code */
fifo_depth = mach64FIFOdepthCTD(cdepth, clock, width);
} else if (si->device_id == MACH64_CT_ID ||
si->device_id == MACH64_ET_ID) {
fifo_depth = mach64FIFOdepthCT(cdepth, clock, width);
} else {
fifo_depth = mach64FIFOdepthDefault(cdepth, clock, width);
}
return(fifo_depth);
}
/*
* mach64ProgramClkMach64CT --
*
*/
void mach64ProgramClkMach64CT(clkCntl, MHz100)
int clkCntl;
int MHz100;
{
char old_crtc_ext_disp;
#ifdef DEBUG
extern void mach64PrintCTPLL();
#endif
int M, N, P, R;
float Q;
int postDiv;
int mhz100 = MHz100;
unsigned char tmp1, tmp2;
int ext_div = 0;
float current_dot_clock;
old_crtc_ext_disp = inb(CRTC_GEN_CNTL+3);
outb(CRTC_GEN_CNTL+3, old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24));
M = si->RefDivider;
R = si->RefFreq;
if (clkCntl > 3) clkCntl = 3;
if (mhz100 < si->MinFreq) mhz100 = si->MinFreq;
if (mhz100 > si->MaxFreq) mhz100 = si->MaxFreq;
Q = (mhz100 * M)/(2.0 * R);
if (false) { /* mach64HasDSP) { */
if (Q > 255) {
/* printf("mach64ProgramClkMach64CT: Warning: Q > 255\n");*/
Q = 255;
P = 0;
postDiv = 1;
} else if (Q > 127.5) {
P = 0;
postDiv = 1;
} else if (Q > 85) {
P = 1;
postDiv = 2;
} else if (Q > 63.75) {
P = 0;
postDiv = 3;
ext_div = 1;
} else if (Q > 42.5) {
P = 2;
postDiv = 4;
} else if (Q > 31.875) {
P = 2;
postDiv = 6;
ext_div = 1;
} else if (Q > 21.25) {
P = 3;
postDiv = 8;
} else if (Q >= 10.6666666667) {
P = 3;
postDiv = 12;
ext_div = 1;
} else {
/* printf("mach64ProgramClkMach64CT: Warning: Q < 10.66666667\n");*/
P = 3;
postDiv = 12;
ext_div = 1;
}
} else {
if (Q > 255) {
/* printf("mach64ProgramClkMach64CT: Warning: Q > 255\n");*/
Q = 255;
P = 0;
}
else if (Q > 127.5)
P = 0;
else if (Q > 63.75)
P = 1;
else if (Q > 31.875)
P = 2;
else if (Q >= 16)
P = 3;
else {
/* printf("mach64ProgramClkMach64CT: Warning: Q < 16\n");*/
P = 3;
}
postDiv = 1 << P;
}
N = (int)(Q * postDiv + 0.5);
current_dot_clock = (2.0 * R * N)/(M * postDiv);
#ifdef DEBUG_HARNESS
printf("Q = %f N = %d P = %d, postDiv = %d R = %d M = %d\n", Q, N, P, postDiv, R, M);
printf("New freq: %.2f\n", (double)((2 * R * N)/(M * postDiv)) / 100.0);
#endif
outb(CLOCK_CNTL + 1, PLL_VCLK_CNTL << 2);
#ifdef DEBUG_HARNESS
printf("CLOCK_CNTL + 1 = 0x%08x\n",PLL_VCLK_CNTL << 2);
#endif
tmp1 = inb(CLOCK_CNTL + 2);
outb(CLOCK_CNTL + 1, (PLL_VCLK_CNTL << 2) | PLL_WR_EN);
outb(CLOCK_CNTL + 2, tmp1 | 0x04);
outb(CLOCK_CNTL + 1, VCLK_POST_DIV << 2);
tmp2 = inb(CLOCK_CNTL + 2);
outb(CLOCK_CNTL + 1, ((VCLK0_FB_DIV + clkCntl) << 2) | PLL_WR_EN);
outb(CLOCK_CNTL + 2, N);
outb(CLOCK_CNTL + 1, (VCLK_POST_DIV << 2) | PLL_WR_EN);
outb(CLOCK_CNTL + 2,
(tmp2 & ~(0x03 << (2 * clkCntl))) | (P << (2 * clkCntl)));
outb(CLOCK_CNTL + 1, (PLL_VCLK_CNTL << 2) | PLL_WR_EN);
outb(CLOCK_CNTL + 2, tmp1 & ~0x04);
if (false) { /* mach64HasDSP) { */
outb(CLOCK_CNTL + 1, PLL_XCLK_CNTL << 2);
tmp1 = inb(CLOCK_CNTL + 2);
outb(CLOCK_CNTL + 1, (PLL_XCLK_CNTL << 2) | PLL_WR_EN);
if (ext_div)
outb(CLOCK_CNTL + 2, tmp1 | (1 << (clkCntl + 4)));
else
outb(CLOCK_CNTL + 2, tmp1 & ~(1 << (clkCntl + 4)));
}
snooze(5000);
(void)inb(DAC_REGS); /* Clear DAC Counter */
outb(CRTC_GEN_CNTL+3, old_crtc_ext_disp);
return;
}
/*
* CalcCRTCRegs --
* Calculate appropiate values for CRTC Registers
*/
void
CalcCRTCRegs(crtcRegs, mode)
mach64CRTCRegPtr crtcRegs;
display_mode *mode;
{
int h_pol = (mode->timing.flags & B_POSITIVE_HSYNC) ? 1 : 0;
int v_pol = (mode->timing.flags & B_POSITIVE_VSYNC) ? 1 : 0;
/* Register CRTC_H_TOTAL_DISP
bits 23-16 Horizontal total visible in character clocks (8 pixel units)
bits 7-0 Horizontal display end in character clocks
*/
crtcRegs->h_total_disp = (((mode->timing.h_display >> 3) - 1) << 16) |
((mode->timing.h_total >> 3) - 1);
/* Register CRTC_H_SYNC_STRT_WID
bits 21 Horizontal sync polarity
bits 20-16 Horizontal sync width in character clocks
bits 10-8 Horizontal sync start delay in pixels
bits 7-0 Horizontal sync start in character clocks
*/
crtcRegs->h_sync_strt_wid = h_pol << 21 |
(((mode->timing.h_sync_end - mode->timing.h_sync_start) >> 3) << 16) |
/* ((mode->timing.h_sync_start % 8) << 8) |*/
((mode->timing.h_sync_start >> 3) - 1);
/* Register CRTC_V_TOTAL_DISP
bits 23-16 Vertical total visible in lines
bits 7-0 Vertical display end in lines
*/
crtcRegs->v_total_disp = ((mode->timing.v_display - 1) << 16) |
(mode->timing.v_total - 1);
/* Register CRTC_V_SYNC_STRT_WID
bits 21 Vertical sync polarity
bits 20-16 Vertical sync width in lines
bits 10-0 Vertical sync start in lines
*/
crtcRegs->v_sync_strt_wid = v_pol << 21 |
((mode->timing.v_sync_end - mode->timing.v_sync_start) << 16) |
(mode->timing.v_sync_start - 1);
switch (mode->space & 0x0fff) {
case B_CMAP8:
crtcRegs->color_depth = CRTC_PIX_WIDTH_8BPP;
break;
case B_RGB15:
crtcRegs->color_depth = CRTC_PIX_WIDTH_15BPP;
break;
case B_RGB16:
crtcRegs->color_depth = CRTC_PIX_WIDTH_16BPP;
break;
case B_RGB32:
crtcRegs->color_depth = CRTC_PIX_WIDTH_32BPP;
break;
default:
}
crtcRegs->crtc_gen_cntl = 0;
if (mode->timing.flags & B_TIMING_INTERLACED)
crtcRegs->crtc_gen_cntl |= CRTC_INTERLACE_EN;
crtcRegs->clock_cntl = 0x03; /* Hack */
crtcRegs->dot_clock = mode->timing.pixel_clock / 10;
crtcRegs->fifo_v1 = mach64FIFOdepth(crtcRegs->color_depth,
crtcRegs->dot_clock, mode->timing.h_display);
}
/*
* SetCRTCRegs
* Write values to the CRTC Registers */
void
SetCRTCRegs(crtcRegs, mode)
mach64CRTCRegPtr crtcRegs;
display_mode *mode;
{
int crtcGenCntl;
uint32 offset;
WaitIdleEmpty();
crtcGenCntl = inw(CRTC_GEN_CNTL);
outw(CRTC_GEN_CNTL, crtcGenCntl & ~(CRTC_EXT_EN | CRTC_LOCK_REGS));
/* Program the clock chip */
mach64ProgramClkMach64CT(0x03, crtcRegs->dot_clock);
/* Horizontal CRTC registers */
outw(CRTC_H_TOTAL_DISP, crtcRegs->h_total_disp);
outw(CRTC_H_SYNC_STRT_WID, crtcRegs->h_sync_strt_wid);
/* Vertical CRTC registers */
outw(CRTC_V_TOTAL_DISP, crtcRegs->v_total_disp);
outw(CRTC_V_SYNC_STRT_WID, crtcRegs->v_sync_strt_wid);
/* Clock select register */
outw(CLOCK_CNTL, crtcRegs->clock_cntl | CLOCK_STROBE);
/* Zero overscan register to insure proper color */
/* outw(OVR_CLR, 0);
outw(OVR_WID_LEFT_RIGHT, 0);
outw(OVR_WID_TOP_BOTTOM, 0);
*/
/* Set the offset and width of the display
We could just use the offset value we already know (1024) but this
will deal with any changes
*/
offset = (si->fbc.frame_buffer - si->framebuffer) >> 3;
outw(CRTC_OFF_PITCH, ((mode->virtual_width >> 3) << 22) | offset);
outw(DST_OFF_PITCH, ((mode->virtual_width >> 3) << 22) | offset);
outw(SRC_OFF_PITCH, ((mode->virtual_width >> 3) << 22) | offset);
/* Display control register -- this one turns on the display */
outw(CRTC_GEN_CNTL,
(crtcGenCntl & 0xff0000ff &
~(CRTC_PIX_BY_2_EN | CRTC_DBL_SCAN_EN | CRTC_INTERLACE_EN |
CRTC_HSYNC_DIS | CRTC_VSYNC_DIS)) |
(crtcRegs->crtc_gen_cntl & ~CRTC_PIX_BY_2_EN) |
crtcRegs->color_depth |
(((crtcRegs->fifo_v1 & 0x0f) << 16) |
CRTC_EXT_DISP_EN | CRTC_EXT_EN));
/* printf(" crtcGenCntl 0x%08x\n", (crtcGenCntl & 0xff0000ff &
~(CRTC_PIX_BY_2_EN | CRTC_DBL_SCAN_EN | CRTC_INTERLACE_EN |
CRTC_HSYNC_DIS | CRTC_VSYNC_DIS)) |
(crtcRegs->crtc_gen_cntl & ~CRTC_PIX_BY_2_EN) |
crtcRegs->color_depth |
(((crtcRegs->fifo_v1 & 0x0f) << 16) |
CRTC_EXT_DISP_EN | CRTC_EXT_EN));*/
/* Set the DAC for the currect mode */
/* mach64SetRamdac(crtcRegs->color_depth, TRUE, crtcRegs->dot_clock);
*/
WaitIdleEmpty();
}
/*
* mach64InitAperture --
* Initialize the aperture for the Mach64.
*/
void mach64InitAperture()
{
ulong apaddr = (ulong) si->framebuffer_pci;
outw(CONFIG_CNTL, ((apaddr/(4*1024*1024)) << 4) | 2);
}
/*
* mach64ResetEngine --
* Resets the GUI engine and clears any FIFO errors.
*/
void mach64ResetEngine()
{
int temp;
/* Ensure engine is not locked up by clearing any FIFO errors */
outw(BUS_CNTL, inw(BUS_CNTL) | BUS_HOST_ERR_ACK | BUS_FIFO_ERR_ACK);
/* Reset engine */
temp = inw(GEN_TEST_CNTL);
outw(GEN_TEST_CNTL, temp & ~GUI_ENGINE_ENABLE);
outw(GEN_TEST_CNTL, temp | GUI_ENGINE_ENABLE);
WaitIdleEmpty();
}
void
InitRAMDAC(uint32 colorDepth, uint32 flags)
{
int count;
uint8 val;
WaitIdleEmpty();
/* Initialize Acceleration Mode */
outb(CRTC_GEN_CNTL+3, ((CRTC_EXT_DISP_EN | CRTC_EXT_EN) >> 24));
switch(colorDepth){
case 8:
val = inb(DAC_CNTL+1);
if (flags & B_8_BIT_DAC)
outb(DAC_CNTL + 1, val | 0x01);
else
outb(DAC_CNTL + 1, val & ~0x01);
break;
case 15:
case 16:
case 24:
case 32:
val = inb(DAC_CNTL + 1);
outb(DAC_CNTL + 1, val | 0x01);
outb(DAC_CNTL + 2, 0xff);
outb(DAC_REGS, 0x00);
for(count = 0; count < 256; count ++) {
outb(DAC_REGS+1, count);
outb(DAC_REGS+1, count);
outb(DAC_REGS+1, count);
}
break;
}
}
@@ -0,0 +1,5 @@
extern void mach64InitAperture();
extern void mach64ResetEngine();
extern void CalcCRTCRegs(mach64CRTCRegPtr, display_mode *);
extern void SetCRTCRegs(mach64CRTCRegPtr, display_mode *);
void InitRAMDAC(uint32, uint32);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,72 @@
/* $XConsortium: mach64fifo.h /main/1 1996/10/27 18:06:29 kaleb $ */
/*
* Copyright 1996 by Kevin E. Martin, Chapel Hill, North Carolina.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of Kevin
* E. Martin not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior
* permission. Kevin E. Martin makes no representations about the
* suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* KEVIN E. MARTIN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
/* $XFree86: xc/programs/Xserver/hw/xfree86/accel/mach64/mach64fifo.h,v 3.1 1996/12/27 06:55:52 dawes Exp $ */
extern int mach64FIFOdepthDefault(
#if NeedFunctionPrototypes
int cdepth,
int clock,
int width
#endif
);
extern int mach64FIFOdepthVTA3(
#if NeedFunctionPrototypes
int cdepth,
int clock,
int width
#endif
);
extern int mach64FIFOdepthVTA4(
#if NeedFunctionPrototypes
int cdepth,
int clock,
int width
#endif
);
extern int mach64FIFOdepthGT(
#if NeedFunctionPrototypes
int cdepth,
int clock,
int width
#endif
);
extern int mach64FIFOdepthCT(
#if NeedFunctionPrototypes
int cdepth,
int clock,
int width
#endif
);
extern int mach64FIFOdepthCTD(
#if NeedFunctionPrototypes
int cdepth,
int clock,
int width
#endif
);
@@ -0,0 +1,399 @@
#include <GraphicsDefs.h>
#include "GlobalData.h"
#include "generic.h"
#define T_POSITIVE_SYNC (B_POSITIVE_HSYNC | B_POSITIVE_VSYNC)
#define MODE_FLAGS (B_SCROLL | B_8_BIT_DAC | B_HARDWARE_CURSOR | B_PARALLEL_ACCESS)
#define MODE_COUNT (sizeof (mode_list) / sizeof (display_mode))
static const display_mode mode_list[] = {
{ { 25175, 640, 656, 752, 800, 480, 490, 492, 525, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(640X480X8.Z1) */
{ { 27500, 640, 672, 768, 864, 480, 488, 494, 530, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* 640X480X60Hz */
{ { 30500, 640, 672, 768, 864, 480, 517, 523, 588, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* SVGA_640X480X60HzNI */
{ { 31500, 640, 664, 704, 832, 480, 489, 492, 520, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(640X480X8.Z1) */
{ { 31500, 640, 656, 720, 840, 480, 481, 484, 500, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(640X480X8.Z1) */
{ { 36000, 640, 696, 752, 832, 480, 481, 484, 509, 0}, B_CMAP8, 640, 480, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(640X480X8.Z1) */
{ { 38100, 800, 832, 960, 1088, 600, 602, 606, 620, 0}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* SVGA_800X600X56HzNI */
{ { 40000, 800, 840, 968, 1056, 600, 601, 605, 628, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(800X600X8.Z1) */
{ { 49500, 800, 816, 896, 1056, 600, 601, 604, 625, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(800X600X8.Z1) */
{ { 50000, 800, 856, 976, 1040, 600, 637, 643, 666, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(800X600X8.Z1) */
{ { 56250, 800, 832, 896, 1048, 600, 601, 604, 631, T_POSITIVE_SYNC}, B_CMAP8, 800, 600, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(800X600X8.Z1) */
{ { 65000, 1024, 1048, 1184, 1344, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1024X768X8.Z1) */
{ { 75000, 1024, 1048, 1184, 1328, 768, 771, 777, 806, 0}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70-72Hz_(1024X768X8.Z1) */
{ { 78750, 1024, 1040, 1136, 1312, 768, 769, 772, 800, T_POSITIVE_SYNC}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1024X768X8.Z1) */
{ { 94500, 1024, 1072, 1168, 1376, 768, 769, 772, 808, T_POSITIVE_SYNC}, B_CMAP8, 1024, 768, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1024X768X8.Z1) */
{ { 94200, 1152, 1184, 1280, 1472, 864, 865, 868, 914, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1152X864X8.Z1) */
{ { 108000, 1152, 1216, 1344, 1600, 864, 865, 868, 900, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1152X864X8.Z1) */
{ { 121500, 1152, 1216, 1344, 1568, 864, 865, 868, 911, T_POSITIVE_SYNC}, B_CMAP8, 1152, 864, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1152X864X8.Z1) */
{ { 108000, 1280, 1328, 1440, 1688, 1024, 1025, 1028, 1066, T_POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1280X1024X8.Z1) */
{ { 135000, 1280, 1296, 1440, 1688, 1024, 1025, 1028, 1066, T_POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1280X1024X8.Z1) */
{ { 157500, 1280, 1344, 1504, 1728, 1024, 1025, 1028, 1072, T_POSITIVE_SYNC}, B_CMAP8, 1280, 1024, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@85Hz_(1280X1024X8.Z1) */
{ { 162000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@60Hz_(1600X1200X8.Z1) */
{ { 175500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@65Hz_(1600X1200X8.Z1) */
{ { 189000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@70Hz_(1600X1200X8.Z1) */
{ { 202500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@75Hz_(1600X1200X8.Z1) */
{ { 216000, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS}, /* Vesa_Monitor_@80Hz_(1600X1200X8.Z1) */
{ { 229500, 1600, 1664, 1856, 2160, 1200, 1201, 1204, 1250, T_POSITIVE_SYNC}, B_CMAP8, 1600, 1200, 0, 0, MODE_FLAGS} /* Vesa_Monitor_@85Hz_(1600X1200X8.Z1) */
};
/* create a mask of one "bits" bits wide */
#define MASKFROMWIDTH(bits) ((1 << bits) - 1)
/*
Validate a target display mode is both
a) a valid display mode for this device and
b) falls between the contraints imposed by "low" and "high"
If the mode is not (or cannot) be made valid for this device, return B_ERROR.
If a valid mode can be constructed, but it does not fall within the limits,
return B_BAD_VALUE.
If the mode is both valid AND falls within the limits, return B_OK.
*/
status_t PROPOSE_DISPLAY_MODE(display_mode *target, const display_mode *low, const display_mode *high) {
const uint16 h_display_bits = MASKFROMWIDTH(8);
const uint16 h_sync_bits = MASKFROMWIDTH(5);
const uint16 h_total_bits = MASKFROMWIDTH(9);
const uint16 v_display_bits = MASKFROMWIDTH(11);
const uint16 v_sync_bits = MASKFROMWIDTH(5);
const uint16 v_total_bits = MASKFROMWIDTH(11);
int i, color_depth;
int mach64CDepths [] = { -1, 4, 8, 15, 16, 24, 32, -1 };
status_t
result = B_ERROR;
uint32
row_bytes,
limit_clock;
double
target_refresh = ((double)target->timing.pixel_clock * 1000.0) / ((double)target->timing.h_total * (double)target->timing.v_total);
bool
want_same_width = target->timing.h_display == target->virtual_width,
want_same_height = target->timing.v_display == target->virtual_height;
/*
NOTE:
Different devices provide different levels of control over the various CRTC values.
This code should be used as a *GUIDELINE ONLY*. The device you're controling may
have very different constraints, and the code below may be insufficient to insure
that a particular display_mode is valid for your device. You would do well to
spend quite a bit of time ensuring that you understand the limitations of your
device, as setting these values incorrectly can create a display_mode that could
turn your monitor into a useless slag of molten components, burn up your card,
lock up the PCI bus, cause partialy or multiply repeating display images, or
otherwise look just plain wierd. Honest.
*/
/*
NOTE:
This code doesn't explicitly support interlaced video modes.
*/
/* validate horizontal timings */
{
/* for most devices, horizontal parameters must be multiples of 8 */
uint16 h_display = target->timing.h_display >> 3;
uint16 h_sync_start = target->timing.h_sync_start >> 3;
uint16 h_sync_end = target->timing.h_sync_end >> 3;
uint16 h_total = target->timing.h_total >> 3;
/* ensure reasonable minium display and sequential order of parms */
if (h_display < (320 >> 3)) h_display = 320 >> 3;
if (h_display > (2048 >> 3)) h_display = 2048 >> 3;
if (h_sync_start < (h_display + 2)) h_sync_start = h_display + 2;
if (h_sync_end < (h_sync_start + 3)) h_sync_end = h_sync_start + 3; /*(0x001f >> 2);*/
if (h_total < (h_sync_end + 1)) h_total = h_sync_end + 1;
/* adjust for register limitations: */
/* h_total is 9 bits */
if (h_total > h_total_bits) h_total = h_total_bits;
/* h_display is 8 bits - handled above */
/* h_sync_start is 9 bits */
/* h_sync_width is 5 bits */
if ((h_sync_end - h_sync_start) > h_sync_bits) h_sync_end = h_sync_start + h_sync_bits;
/* shift back to the full width values */
target->timing.h_display = h_display << 3;
target->timing.h_sync_start = h_sync_start << 3;
target->timing.h_sync_end = h_sync_end << 3;
target->timing.h_total = h_total << 3;
}
/* did we fall out of one of the limits? */
if (
(target->timing.h_display < low->timing.h_display) ||
(target->timing.h_display > high->timing.h_display) ||
(target->timing.h_sync_start < low->timing.h_sync_start) ||
(target->timing.h_sync_start > high->timing.h_sync_start) ||
(target->timing.h_sync_end < low->timing.h_sync_end) ||
(target->timing.h_sync_end > high->timing.h_sync_end) ||
(target->timing.h_total < low->timing.h_total) ||
(target->timing.h_total > high->timing.h_total)
) result = B_BAD_VALUE;
/* validate vertical timings */
{
uint16 v_display = target->timing.v_display;
uint16 v_sync_start = target->timing.v_sync_start;
uint16 v_sync_end = target->timing.v_sync_end;
uint16 v_total = target->timing.v_total;
/* ensure reasonable minium display and sequential order of parms */
/* v_display is 11 bits */
/* v_total is 11 bits */
/* v_sync_start is 11 bits */
/* v_sync_width is 5 bits */
if (v_display < 200) v_display = 200;
if (v_display > (v_display_bits - 5)) v_display = (v_display_bits - 5); /* leave room for the sync pulse */
if (v_sync_start < (v_display + 1)) v_sync_start = v_display + 1;
if (v_sync_end < v_sync_start) v_sync_end = v_sync_start + 3;
if (v_total < (v_sync_end + 1)) v_total = v_sync_end + 1;
/* adjust for register limitations */
if ((v_sync_end - v_sync_start) > v_sync_bits) v_sync_end = v_sync_start + v_sync_bits;
target->timing.v_display = v_display;
target->timing.v_sync_start = v_sync_start;
target->timing.v_sync_end = v_sync_end;
target->timing.v_total = v_total;
}
/* did we fall out of one of the limits? */
if (
(target->timing.v_display < low->timing.v_display) ||
(target->timing.v_display > high->timing.v_display) ||
(target->timing.v_sync_start < low->timing.v_sync_start) ||
(target->timing.v_sync_start > high->timing.h_sync_start) ||
(target->timing.v_sync_end < low->timing.v_sync_end) ||
(target->timing.v_sync_end > high->timing.v_sync_end) ||
(target->timing.v_total < low->timing.v_total) ||
(target->timing.v_total > high->timing.v_total)
) result = B_BAD_VALUE;
/* adjust pixel clock for DAC limits and target refresh rate */
/*
We're re-calcuating the pixel_clock here because we might have
changed the h/v totals above. If we didn't change anything
the calculation is wasted, but it's no big deal.
*/
target->timing.pixel_clock = target_refresh * ((double)target->timing.h_total) * ((double)target->timing.v_total) / 1000.0 + .5;
/*
Select the maximum pixel clock based on the color space. Your
device may have other constraints. In this sample driver, we
calculated the maximum pixel clock for this device in the
initialization code.
This is also a convienient place to determine the number of bytes
per pixel for a later display pitch calculation.
*/
switch (target->space & 0x0fff) {
case B_CMAP8:
limit_clock = si->pix_clk_max8;
row_bytes = 1;
color_depth = 8;
break;
case B_RGB15:
case B_RGB16:
limit_clock = si->pix_clk_max16;
row_bytes = 2;
color_depth = 16;
break;
case B_RGB32:
limit_clock = si->pix_clk_max32;
row_bytes = 4;
color_depth = 32;
break;
default:
/* no amount of adjusting will fix not being able to support the pixel format */
return B_ERROR;
}
/* make sure we don't generate more pixel bandwidth than the device can handle */
if (target->timing.pixel_clock > limit_clock) target->timing.pixel_clock = limit_clock;
/* we probably ought to check against too low of a pixel rate, but I'm lazy */
/* note if we fell outside the limits */
if (
(target->timing.pixel_clock < low->timing.pixel_clock) ||
(target->timing.pixel_clock > high->timing.pixel_clock)
) return B_ERROR;
/* validate display vs. virtual */
if ((target->timing.h_display > target->virtual_width) || want_same_width)
target->virtual_width = target->timing.h_display;
if ((target->timing.v_display > target->virtual_height) || want_same_height)
target->virtual_height = target->timing.v_display;
if (target->virtual_width > 2048)
target->virtual_width = 2048;
/* adjust virtual width for engine limitations */
target->virtual_width = (target->virtual_width + 7) & ~7;
if (
(target->virtual_width < low->virtual_width) ||
(target->virtual_width > high->virtual_width)
) result = B_BAD_VALUE;
/* calculate rowbytes after we've nailed the virtual width */
row_bytes *= target->virtual_width;
/* memory requirement for frame buffer */
if ((row_bytes * target->virtual_height) > si->mem_size)
target->virtual_height = si->mem_size / row_bytes;
if (target->virtual_height > 2048)
target->virtual_height = 2048;
if (target->virtual_height < target->timing.v_display){
/* not enough frame buffer memory for the mode */
return B_ERROR;}
else if (
(target->virtual_height < low->virtual_height) ||
(target->virtual_height > high->virtual_height)
) result = B_BAD_VALUE;
/*
Bit Flag Encoding
The way the bit flags works is as follows:
low high meaning
--- ---- -------
0 0 Feature must NOT be enabled
0 1 Feature MAY be enabled, prefered enabled
1 0 Feature MAY be enabled, prefered disabled
1 1 Feature MUST be enabled
*/
/* MORE WORK REQUIRED HERE. Current drivers mostly ignore the flags */
/* Look to see if there if the mode is doable */
if (result != B_BAD_VALUE)
for(i = 0; si->Freq_Table[i].h_disp != 0; i++) {
if ((si->Freq_Table[i].h_disp << 3 >= target->timing.h_display) &&
(si->Freq_Table[i].dacmask >= si->DAC_SubType) &&
(si->Freq_Table[i].max_dot_clock >= (target->timing.pixel_clock / 1000.0)) &&
(mach64CDepths[si->Freq_Table[i].color_depth & 0x07] >= color_depth)){
result = B_OK;
break;
}
}
return result;
}
/*
Return the number of modes this device will return from GET_MODE_LIST().
*/
uint32 ACCELERANT_MODE_COUNT(void) {
/* return the number of 'built-in' display modes */
return si->mode_count;
}
/*
Copy the list of guaranteed supported video modes to the location
provided.
*/
status_t GET_MODE_LIST(display_mode *dm) {
/* copy them to the buffer pointed at by *dm */
memcpy(dm, atimach64_mode_list, si->mode_count * sizeof(display_mode));
return B_OK;
}
status_t get_first_mode(display_mode *dm) {
/* copy first valid mode to the buffer pointed at by *dm */
memcpy(dm, atimach64_mode_list, sizeof(display_mode));
return B_OK;
}
/*
Create a list of display_modes to pass back to the caller.
*/
status_t create_mode_list(void) {
size_t max_size;
uint32
i, j,
pix_clk_range;
const display_mode
*src;
display_mode
*dst,
low,
high;
/*
We prefer frame buffers to have the same endianness as the host CPU, but it's
not required. You can even return both, although there isn't a way for the
current Screen preferences panel to allow the user to choose which one to use.
*/
#if defined(__INTEL__)
color_space spaces[4] = {B_CMAP8, B_RGB15_LITTLE, B_RGB16_LITTLE, B_RGB32_LITTLE};
#else
color_space spaces[4] = {B_CMAP8, B_RGB15_BIG, B_RGB16_BIG, B_RGB32_BIG};
#endif
/* printf(" Mode count %ld \n", MODE_COUNT);*/
/* figure out how big the list could be, and adjust up to nearest multiple of B_PAGE_SIZE */
max_size = (((MODE_COUNT * 4) * sizeof(display_mode)) + (B_PAGE_SIZE-1)) & ~(B_PAGE_SIZE-1);
/* create an area to hold the info */
si->mode_area = atimach64_mode_list_area =
create_area("ATIMACH64 accelerant mode info", (void **)&atimach64_mode_list, B_ANY_ADDRESS, max_size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
if (atimach64_mode_list_area < B_OK) return atimach64_mode_list_area;
/* walk through our predefined list and see which modes fit this device */
src = mode_list;
dst = atimach64_mode_list;
si->mode_count = 0;
for (i = 0; i < MODE_COUNT; i++) {
/* set ranges for acceptable values */
low = high = *src;
/* range is 6.25% of default clock: arbitrarily picked */
pix_clk_range = low.timing.pixel_clock >> 5;
low.timing.pixel_clock -= pix_clk_range;
high.timing.pixel_clock += pix_clk_range;
/* some cards need wider virtual widths for certain modes */
high.virtual_width = 2048;
/* do it once for each depth we want to support */
for (j = 0; j < (sizeof(spaces) / sizeof(color_space)); j++) {
/* set target values */
*dst = *src;
/* poke the specific space */
dst->space = low.space = high.space = spaces[j];
/* ask for a compatible mode */
if (PROPOSE_DISPLAY_MODE(dst, &low, &high) != B_ERROR) {
/* count it, and move on to next mode */
dst++;
si->mode_count++;
}
}
/* advance to next mode */
src++;
}
return B_OK;
}
@@ -0,0 +1,285 @@
#include <GraphicsDefs.h>
#include "GlobalData.h"
#include "generic.h"
#include "Mach64.h"
#include <sys/ioctl.h>
/*
Enable/Disable interrupts. Just a wrapper around the
ioctl() to the kernel driver.
*/
static void interrupt_enable(bool flag) {
status_t result;
atimach64_set_bool_state sbs;
/* set the magic number so the driver knows we're for real */
sbs.magic = ATIMACH64_PRIVATE_DATA_MAGIC;
sbs.do_it = flag;
/* contact driver and get a pointer to the registers and shared data */
result = ioctl(fd, ATIMACH64_RUN_INTERRUPTS, &sbs, sizeof(sbs));
}
/*
Calculates the number of bits for a given color_space.
Usefull for mode setup routines, etc.
*/
uint32 calcBitsPerPixel(uint32 cs) {
uint32 bpp = 0;
switch (cs) {
case B_RGB32_BIG:
case B_RGBA32_BIG:
case B_RGB32_LITTLE:
case B_RGBA32_LITTLE:
bpp = 32; break;
case B_RGB24_BIG:
case B_RGB24_LITTLE:
bpp = 24; break;
case B_RGB16_BIG:
case B_RGB16_LITTLE:
bpp = 16; break;
case B_RGB15_BIG:
case B_RGBA15_BIG:
case B_RGB15_LITTLE:
case B_RGBA15_LITTLE:
bpp = 15; break;
case B_CMAP8:
bpp = 8; break;
}
return bpp;
}
/*
The code to actually configure the display. Unfortunately, there's not much
that can be provided in the way of sample code. If you're lucky, you're writing
a driver for a device that has all (or at least most) of the bits for a particular
configuration value in the same register, rather than spread out over the standard
VGA registers + a zillion expansion bits. In any case, I've found that the way
to simplify this routine is to do all of the error checking in PROPOSE_DISPLAY_MODE(),
and just assume that the values I get here are acceptable.
*/
static void do_set_display_mode(display_mode *dm) {
mach64CRTCRegRec Regs;
CalcCRTCRegs(&Regs, dm);
/* disable interrupts using the kernel driver */
interrupt_enable(false);
SetCRTCRegs(&Regs, dm);
/* Initialize RAMDAC */
InitRAMDAC(calcBitsPerPixel(dm->space), dm->flags);
/* enable interrupts using the kernel driver */
interrupt_enable(true);
}
/*
The exported mode setting routine. First validate the mode, then call our
private routine to hammer the registers.
*/
status_t SET_DISPLAY_MODE(display_mode *mode_to_set) {
display_mode bounds, target;
/* ask for the specific mode */
target = bounds = *mode_to_set;
if (PROPOSE_DISPLAY_MODE(&target, &bounds, &bounds) == B_ERROR)
return B_ERROR;
do_set_display_mode(&target);
/* update shared info */
target.h_display_start = 0;
target.v_display_start = 0;
si->dm = target;
/* calculate bytes per row ans set it */
si->fbc.bytes_per_row = ((calcBitsPerPixel(target.space) + 1) / 8)
*target.virtual_width;
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) {
int byte_offset;
/*
Many devices have limitations on the granularity of the horizontal offset.
Make any checks for this here. A future revision of the driver API will
add a hook to return the granularity for a given display mode.
*/
/* most cards can handle multiples of 8 */
if (h_display_start & 0x07)
return B_ERROR;
/* do not run past end of display */
if ((si->dm.timing.h_display + h_display_start) > si->dm.virtual_width)
return B_ERROR;
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;
/* Calculate offset remebering to include the cursor area */
byte_offset = (si->fbc.frame_buffer - si->framebuffer) >> 3;
byte_offset += ((h_display_start + v_display_start*si->dm.virtual_width) *
(calcBitsPerPixel(si->dm.space) / 8)) >> 3;
/* actually set the registers */
if (~(si->dm.flags & B_HARDWARE_CURSOR))
SHOW_CURSOR(false);
outw(CRTC_OFF_PITCH, (inw(CRTC_OFF_PITCH) & 0xfff00000) | byte_offset);
return B_OK;
}
/*
Set the indexed color palette.
*/
void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags) {
/*
Some cards use the indexed color regisers in the DAC for gamma correction.
If this is true with your device (and it probably is), you need to protect
against setting these registers when not in an indexed mode.
*/
if (si->dm.space != B_CMAP8) return;
/*
There isn't any need to keep a copy of the data being stored, as the app_server
will set the colors each time it switches to an 8bpp mode, or takes ownership
of an 8bpp mode after a GameKit app has used it.
*/
/*
You're on your own from here. Chances are your code will look something like
what follows:
*/
/*
Due to the nature of the SAMPLE card, we need to do bytewise writes
to the dac regs. Stupid, but there you go. So, we do it like we're
supposed to.
*/
WaitQueue(1);
outb(DAC_REGS, first);
while (count--) {
outb(DAC_REGS+1, *color_data++);
outb(DAC_REGS+1, *color_data++);
outb(DAC_REGS+1, *color_data++);
}
}
/* masks for DPMS control bits */
enum {
H_SYNC_OFF = 0x01,
V_SYNC_OFF = 0x02,
DISPLAY_OFF = 0x04,
BITSMASK = H_SYNC_OFF | V_SYNC_OFF | DISPLAY_OFF
};
/*
Put the display into one of the Display Power Management modes.
*/
status_t SET_DPMS_MODE(uint32 dpms_flags) {
uint32 LTemp;
/*
The register containing the horizontal and vertical sync control bits
usually contains other control bits, so do a read-modify-write.
*/
/* read bits */
LTemp = inw(CRTC_GEN_CNTL);
/* mask them */
LTemp &= ~(CRTC_HSYNC_DIS | CRTC_VSYNC_DIS); /* clear all disable bits (including display disable) */
/* now pick one of the DPMS configurations */
switch(dpms_flags) {
case B_DPMS_ON: /* H: on, V: on */
/* do nothing, bits already clear */
/* usually, but it may be different for your device */
break;
case B_DPMS_STAND_BY: /* H: off, V: on, display off */
LTemp |= CRTC_HSYNC_DIS;
break;
case B_DPMS_SUSPEND: /* H: on, V: off, display off */
LTemp |= CRTC_VSYNC_DIS;
break;
case B_DPMS_OFF: /* H: off, V: off, display off */
LTemp |= CRTC_HSYNC_DIS | CRTC_VSYNC_DIS;
break;
default:
return B_ERROR;
}
/* write the bits */
outw(CRTC_GEN_CNTL, LTemp);
snooze(10000);
/*
NOTE: if you're driving a device with a backlight (like a digital LCD),
you may want to turn off the backlight here when in a DPMS power saving mode.
*/
if (dpms_flags == B_DPMS_ON)
/* turn on the back light */
;
else
/* turn off the back light */
;
return B_OK;
}
/*
Report device DPMS capabilities. Most newer cards can do it all.
I've only seen one older card that can do a subset (ON and OFF).
Very early cards may not be able to do this at all.
*/
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) {
uint32 LTemp;
uint32 mode = B_DPMS_ON;
/* read the control bits from the device */
/* read the bits */
LTemp = inw(CRTC_GEN_CNTL);
/* what mode is set? */
switch (LTemp & (CRTC_HSYNC_DIS | CRTC_VSYNC_DIS)) {
case 0: /* H: on, V: on */
mode = B_DPMS_ON;
break;
case CRTC_HSYNC_DIS: /* H: off, V: on */
mode = B_DPMS_STAND_BY;
break;
case CRTC_VSYNC_DIS: /* H: on, V: off */
mode = B_DPMS_SUSPEND;
break;
case (CRTC_HSYNC_DIS | CRTC_VSYNC_DIS): /* H: off, V: off */
mode = B_DPMS_OFF;
break;
}
return mode;
}
@@ -0,0 +1,46 @@
#if !defined(GENERIC_H)
#define GENERIC_H
#include <Accelerant.h>
status_t INIT_ACCELERANT(int fd);
ssize_t ACCELERANT_CLONE_INFO_SIZE(void);
void GET_ACCELERANT_CLONE_INFO(void *data);
status_t CLONE_ACCELERANT(void *data);
void UNINIT_ACCELERANT(void);
status_t GET_ACCELERANT_DEVICE_INFO(accelerant_device_info *adi);
sem_id ACCELERANT_RETRACE_SEMAPHORE(void);
uint32 ACCELERANT_MODE_COUNT(void);
status_t GET_MODE_LIST(display_mode *dm);
status_t PROPOSE_DISPLAY_MODE(display_mode *target, const display_mode *low, const display_mode *high);
status_t SET_DISPLAY_MODE(display_mode *mode_to_set);
status_t GET_DISPLAY_MODE(display_mode *current_mode);
status_t GET_FRAME_BUFFER_CONFIG(frame_buffer_config *a_frame_buffer);
status_t GET_PIXEL_CLOCK_LIMITS(display_mode *dm, uint32 *low, uint32 *high);
status_t MOVE_DISPLAY(uint16 h_display_start, uint16 v_display_start);
status_t GET_TIMING_CONSTRAINTS(display_timing_constraints *dtc);
void SET_INDEXED_COLORS(uint count, uint8 first, uint8 *color_data, uint32 flags);
uint32 DPMS_CAPABILITIES(void);
uint32 DPMS_MODE(void);
status_t SET_DPMS_MODE(uint32 dpms_flags);
status_t SET_CURSOR_SHAPE(uint16 width, uint16 height, uint16 hot_x, uint16 hot_y, uint8 *andMask, uint8 *xorMask);
void MOVE_CURSOR(uint16 x, uint16 y);
void SHOW_CURSOR(bool is_visible);
uint32 ACCELERANT_ENGINE_COUNT(void);
status_t ACQUIRE_ENGINE(uint32 capabilities, uint32 max_wait, sync_token *st, engine_token **et);
status_t RELEASE_ENGINE(engine_token *et, sync_token *st);
void WAIT_ENGINE_IDLE(void);
status_t GET_SYNC_TOKEN(engine_token *et, sync_token *st);
status_t SYNC_TO_TOKEN(sync_token *st);
void SCREEN_TO_SCREEN_BLIT(engine_token *et, blit_params *list, uint32 count);
void FILL_RECTANGLE(engine_token *et, uint32 color, fill_rect_params *list, uint32 count);
void INVERT_RECTANGLE(engine_token *et, fill_rect_params *list, uint32 count);
void FILL_SPAN(engine_token *et, uint32 color, uint16 *list, uint32 count);
#endif
@@ -6,3 +6,5 @@ SubInclude OBOS_TOP src add-ons kernel drivers graphics neomagic ;
SubInclude OBOS_TOP src add-ons kernel drivers graphics nvidia ;
SubInclude OBOS_TOP src add-ons kernel drivers graphics radeon ;
SubInclude OBOS_TOP src add-ons kernel drivers graphics tdfx ;
SubIncludeGPL OBOS_TOP src add-ons kernel drivers graphics atimach64 ;
@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
@@ -0,0 +1,19 @@
SubDir OBOS_TOP src add-ons kernel drivers graphics atimach64 ;
SubDirCcFlags -DSEMAPHORE=1 ;
UsePrivateHeaders graphics ;
UsePrivateHeaders [ FDirName graphics atimach64 ] ;
R5KernelAddon atimach64.driver : kernel drivers bin :
driver.c
m64info.c
;
# Link to kernel/drivers/dev/graphics
{
local dir = [ FDirName $(OBOS_ADDON_DIR) kernel drivers dev graphics ] ;
local instDriver = <kernel!drivers!dev!graphics>atimach64.driver ;
MakeLocate $(instDriver) : $(dir) ;
RelSymLink $(instDriver) : atimach64.driver ;
}
@@ -0,0 +1,976 @@
/*
Driver for ATIMach64VT
Lots of rewriting to be able to work with other ATIMACH64 cards
Rene MacKinney <rene_@freenet.co.uk>
6.May.1999
Based on GraphicDriverKit for R4 alpha release
Huge thanks to Trey for all the help
*/
/* standard kernel driver stuff */
#include <KernelExport.h>
#include <PCI.h>
#include <OS.h>
#include <malloc.h>
/* this is for the standardized portion of the driver API */
/* currently only one operation is defined: B_GET_ACCELERANT_SIGNATURE */
#include <graphic_driver.h>
/* this is for sprintf() */
#include <stdio.h>
/* this is for string compares */
#include <string.h>
/* The private interface between the accelerant and the kernel driver. */
#include "DriverInterface.h"
/* ATI Mach64 Definitions */
#include "regmach64.h"
#include "ATIMach64.h"
#if DEBUG > 0
#define ddprintf(a) dprintf a
#else
#define ddprintf(a)
#endif
#define get_pci(o, s) (*pci_bus->read_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s))
#define set_pci(o, s, v) (*pci_bus->write_pci_config)(pcii->bus, pcii->device, pcii->function, (o), (s), (v))
/* Given the fact that the VT is a motherboard integrated
card we only support one */
#define MAX_DEVICES 1
/* The vertical retrace semaphore is not implemented instead of erasing
all the code about it we simply don't compile it. Waiting for the
implementation */
#define SEMAPHORE 0
/* Tell the kernel what revision of the driver API we support */
int32 api_version = 2;
/* these structures are private to the kernel driver */
typedef struct device_info device_info;
#if defined(POST_R4_0)
typedef struct {
timer te; /* timer entry for add_timer() */
device_info *di; /* pointer to the owning device */
bigtime_t when_target; /* when we're supposed to wake up */
} timer_info;
#endif
struct device_info {
uint32 is_open; /* a count of how many times the devices has been opened */
area_id shared_area; /* the area shared between the driver and all of the accelerants */
shared_info *si; /* a pointer to the shared area, for convenience */
vuint32 *regs; /* kernel's pointer to memory mapped registers */
int32 can_interrupt; /* when we're faking interrupts, let's us know if we should generate one */
#if defined(POST_R4_0)
timer_info ti_a; /* a pool of two timer managment buffers */
timer_info ti_b;
timer_info *current_timer; /* the timer buffer that's currently in use */
#else
thread_id tid;
#endif
#if DEBUG > 0
uint32 interrupt_count; /* if we're debugging, a count of how many times the interrupt handler has
been called for this device */
#endif
pci_info pcii; /* a convenience copy of the pci info for this device */
char name[B_OS_NAME_LENGTH]; /* where we keep the name of the device for publishing and comparing */
};
typedef struct {
#if DEBUG > 0
uint32 total_interrupts; /* total number of interrupts seen by our handler */
#endif
uint32 count; /* number of devices actually found */
benaphore kernel; /* for serializing opens/closes */
char *device_names[MAX_DEVICES+1]; /* device name pointer storage */
device_info di[MAX_DEVICES]; /* device specific stuff */
} DeviceData;
/* prototypes for our private functions */
static status_t open_hook (const char* name, uint32 flags, void** cookie);
static status_t close_hook (void* dev);
static status_t free_hook (void* dev);
static status_t read_hook (void* dev, off_t pos, void* buf, size_t* len);
static status_t write_hook (void* dev, off_t pos, const void* buf, size_t* len);
static status_t control_hook (void* dev, uint32 msg, void *buf, size_t len);
static status_t map_device(device_info *di);
static void unmap_device(device_info *di);
static void probe_devices(void);
static int32 atimach64_interrupt(void *data);
#if DEBUG > 0
static int dump(int argc, char **argv);
#endif
static DeviceData *pd;
static pci_module_info *pci_bus;
static device_hooks graphics_device_hooks = {
open_hook,
close_hook,
free_hook,
control_hook,
read_hook,
write_hook,
NULL,
NULL,
NULL,
NULL
};
static uint16 ati_device_list[] = {
PCI_MACH64_VT_ID, /* Mach64 VT device */
0
};
static struct {
uint16 vendor;
uint16 *devices;
} SupportedDevices[] = {
{PCI_ATI_VENDOR_ID, ati_device_list},
{0x0000, NULL}
};
/*
init_hardware() - Returns B_OK if one is
found, otherwise returns B_ERROR so the driver will be unloaded.
*/
status_t
init_hardware(void) {
long pci_index = 0;
pci_info pcii;
bool found_one = FALSE;
/* choke if we can't find the PCI bus */
if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci_bus) != B_OK)
return B_ERROR;
/* while there are more pci devices */
while ((*pci_bus->get_nth_pci_info)(pci_index, &pcii) == B_NO_ERROR) {
int vendor = 0;
ddprintf(("SKD init_hardware(): checking pci index %ld, device 0x%04x/0x%04x\n", pci_index, pcii.vendor_id, pcii.device_id));
/* if we match a supported vendor */
while (SupportedDevices[vendor].vendor) {
if (SupportedDevices[vendor].vendor == pcii.vendor_id) {
uint16 *devices = SupportedDevices[vendor].devices;
/* while there are more supported devices */
while (*devices) {
/* if we match a supported device */
if (*devices == pcii.device_id ) {
ddprintf(("SKD: we support this device\n"));
found_one = TRUE;
goto done;
}
/* next supported device */
devices++;
}
}
vendor++;
}
/* next pci_info struct, please */
pci_index++;
}
ddprintf(("SKD: init_hardware - no supported devices\n"));
done:
/* put away the module manager */
put_module(B_PCI_MODULE_NAME);
return (found_one ? B_OK : B_ERROR);
}
status_t
init_driver(void) {
/* get a handle for the pci bus */
if (get_module(B_PCI_MODULE_NAME, (module_info **)&pci_bus) != B_OK)
return B_ERROR;
/* driver private data */
pd = (DeviceData *)calloc(1, sizeof(DeviceData));
if (!pd) {
put_module(B_PCI_MODULE_NAME);
return B_ERROR;
}
/* initialize the benaphore */
INIT_BEN(pd->kernel);
/* find all of our supported devices */
probe_devices();
#if DEBUG > 0
add_debugger_command("dump", dump, "dump ATIMach64 kernel driver persistant data");
#endif
return B_OK;
}
const char **
publish_devices(void) {
/* return the list of supported devices */
return (const char **)pd->device_names;
}
device_hooks *
find_device(const char *name) {
int index = 0;
while (pd->device_names[index]) {
if (strcmp(name, pd->device_names[index]) == 0)
return &graphics_device_hooks;
index++;
}
return NULL;
}
void
uninit_driver(void) {
#if DEBUG > 0
remove_debugger_command("dump", dump);
#endif
/* free the driver data */
DELETE_BEN(pd->kernel);
free(pd);
pd = NULL;
/* put the pci module away */
put_module(B_PCI_MODULE_NAME);
}
static status_t map_device(device_info *di) {
/* default: frame buffer in [0], control regs in [1] */
/* int regs = 1; */
int fb = 0;
char buffer[B_OS_NAME_LENGTH];
shared_info *si = di->si;
uint32 tmpUlong;
pci_info *pcii = &(di->pcii);
uint32 rom_base;
/* enable memory mapped IO, disable VGA I/O */
tmpUlong = get_pci(PCI_command, 4);
tmpUlong |= PCI_command_memory;
tmpUlong &= ~((uint32) PCI_command_io);
set_pci(PCI_command, 4, tmpUlong);
/* enable ROM decoding */
/* tmpUlong = get_pci(PCI_rom_base, 4);
tmpUlong |= PCI_rom_enable;
set_pci(PCI_rom_base, 4, tmpUlong); */
/* map the rom */
/* If rom_base points to 0x00 try using standard ROM location for Mach64 */
rom_base = (di->pcii.u.h0.rom_base == 0) ? 0x000c0000 : di->pcii.u.h0.rom_base;
sprintf(buffer, "%04X_%04X_%02X%02X%02X rom",
di->pcii.vendor_id, di->pcii.device_id,
di->pcii.bus, di->pcii.device, di->pcii.function);
si->rom_area = map_physical_memory(
buffer,
(void *)rom_base,
di->pcii.u.h0.rom_size,
B_ANY_KERNEL_ADDRESS,
B_READ_AREA,
(void **)&(si->rom));
/* return the error if there was some problem */
if (si->rom_area < 0) {
delete_area(si->regs_area);
si->regs_area = -1;
return si->rom_area;
}
sprintf(buffer, "%04X_%04X_%02X%02X%02X framebuffer",
di->pcii.vendor_id, di->pcii.device_id,
di->pcii.bus, di->pcii.device, di->pcii.function);
si->fb_area = map_physical_memory(
buffer,
(void *) di->pcii.u.h0.base_registers[fb],
di->pcii.u.h0.base_register_sizes[fb],
#if defined(__INTEL__)
#if defined(POST_R4_0)
B_ANY_KERNEL_BLOCK_ADDRESS | B_MTR_WC,
#else
B_ANY_KERNEL_ADDRESS | B_MTR_WC,
#endif
#else
B_ANY_KERNEL_BLOCK_ADDRESS,
#endif
B_READ_AREA + B_WRITE_AREA,
&(si->framebuffer));
#if defined(__INTEL__)
if (si->fb_area < 0) {
/* try to map this time without write combining */
/*
After R4.0 (Intel), map_physical_memory() will try B_ANY_KERNEL_ADDRESS if
a call with B_ANY_KERNEL_BLOCK_ADDRESS would fail. It always worked this way
under PPC.
*/
si->fb_area = map_physical_memory(
buffer,
(void *) di->pcii.u.h0.base_registers[fb],
di->pcii.u.h0.base_register_sizes[fb],
#if defined(POST_R4_0)
B_ANY_KERNEL_BLOCK_ADDRESS,
#else
B_ANY_KERNEL_ADDRESS,
#endif
B_READ_AREA + B_WRITE_AREA,
&(si->framebuffer));
}
#endif
/* if there was an error, delete our other areas */
if (si->fb_area < 0) {
delete_area(si->regs_area);
si->regs_area = -1;
delete_area(si->rom_area);
si->rom_area = -1;
}
/* remember the DMA address of the frame buffer for BDirectWindow purposes */
si->framebuffer_pci = (void *) di->pcii.u.h0.base_registers_pci[fb];
return si->fb_area;
}
static void
unmap_device(device_info *di) {
shared_info *si = di->si;
uint32 tmpUlong;
pci_info *pcii = &(di->pcii);
ddprintf(("unmap_device(%08lx) begins...\n", (uint32)di));
ddprintf((" regs_area: %ld\n fb_area: %ld\n", si->regs_area, si->fb_area));
/* disable memory mapped IO */
tmpUlong = get_pci(PCI_command, 4);
tmpUlong &= 0xfffffffc;
set_pci(PCI_command, 4, tmpUlong);
/* disable ROM decoding */
tmpUlong = get_pci(PCI_rom_base, 4);
tmpUlong &= 0xfffffffe;
set_pci(PCI_rom_base, 4, tmpUlong);
/* delete the areas */
if (si->rom_area >= 0) delete_area(si->rom_area);
/* if (si->regs_area >= 0) delete_area(si->regs_area);*/
if (si->fb_area >= 0) delete_area(si->fb_area);
si->rom_area = si->regs_area = si->fb_area = -1;
si->framebuffer = NULL;
di->regs = NULL;
si->rom = NULL;
ddprintf(("unmap_device() ends.\n"));
}
static void
probe_devices(void) {
uint32 pci_index = 0;
uint32 count = 0;
device_info *di = pd->di;
/* while there are more pci devices */
while ((count < MAX_DEVICES) && ((*pci_bus->get_nth_pci_info)(pci_index, &(di->pcii)) == B_NO_ERROR)) {
int vendor = 0;
/* if we match a supported vendor */
while (SupportedDevices[vendor].vendor) {
if (SupportedDevices[vendor].vendor == di->pcii.vendor_id) {
uint16 *devices = SupportedDevices[vendor].devices;
/* while there are more supported devices */
while (*devices) {
/* if we match a supported device */
if (*devices == di->pcii.device_id ) {
/* publish the device name */
sprintf(di->name, "graphics/%04X_%04X_%02X%02X%02X",
di->pcii.vendor_id, di->pcii.device_id,
di->pcii.bus, di->pcii.device, di->pcii.function);
ddprintf(("SKD: making /dev/%s\n", di->name));
/* remember the name */
pd->device_names[count] = di->name;
/* mark the driver as available for R/W open */
di->is_open = 0;
/* mark areas as not yet created */
di->shared_area = -1;
/* mark pointer to shared data as invalid */
di->si = NULL;
/* inc pointer to device info */
di++;
/* inc count */
count++;
/* break out of these while loops */
goto next_device;
}
/* next supported device */
devices++;
}
}
vendor++;
}
next_device:
/* next pci_info struct, please */
pci_index++;
}
/* propagate count */
pd->count = count;
/* terminate list of device names with a null pointer */
pd->device_names[pd->count] = NULL;
ddprintf(("SKD probe_devices: %ld supported devices\n", pd->count));
}
static uint32
thread_interrupt_work(int32 *flags, vuint32 *regs, shared_info *si) {
uint32 handled = B_HANDLED_INTERRUPT;
/* release the vblank semaphore */
if (si->vblank >= 0) {
int32 blocked;
if ((get_sem_count(si->vblank, &blocked) == B_OK) && (blocked < 0)) {
release_sem_etc(si->vblank, -blocked, B_DO_NOT_RESCHEDULE);
handled = B_INVOKE_SCHEDULER;
}
}
return handled;
}
static int32
atimach64_interrupt(void *data)
{
int32 handled = B_UNHANDLED_INTERRUPT;
device_info *di = (device_info *)data;
shared_info *si = di->si;
int32 *flags = &(si->flags);
vuint32 *regs;
#if DEBUG > 0
pd->total_interrupts++;
#endif
/* is someone already handling an interrupt for this device? */
if (atomic_or(flags, SKD_HANDLER_INSTALLED) & SKD_HANDLER_INSTALLED) {
#if DEBUG > 0
kprintf("SKD: Already in handler!\n");
#endif
goto exit0;
}
/* get regs */
regs = di->regs;
/* read the interrrupt status register */
/* did this card cause an interrupt */
if (0 /* replace this expression */) {
/* do our stuff */
handled = thread_interrupt_work(flags, regs, si);
#if DEBUG > 0
/* increment the counter for this device */
di->interrupt_count++;
#endif
/* clear the interrupt status */
}
/* note that we're not in the handler any more */
atomic_and(flags, ~SKD_HANDLER_INSTALLED);
exit0:
return handled;
}
#if SEMAPHORE
#if defined(POST_R4_0)
static int32 timer_interrupt_func(timer *te, uint32 pc) {
bigtime_t now = system_time();
/* get the pointer to the device we're handling this time */
device_info *di = ((timer_info *)te)->di;
shared_info *si = di->si;
int32 *flags = &(si->flags);
vuint32 *regs = di->regs;
uint32 vbl_status = 0 /* read vertical blank status */;
int32 result = B_HANDLED_INTERRUPT;
/* are we suppoesed to handle interrupts still? */
if (atomic_and(flags, -1) & SKD_HANDLER_INSTALLED) {
/* reschedule with same period by default */
bigtime_t when = si->refresh_period;
timer *to;
/* if interrupts are "enabled", do our thing */
if (di->can_interrupt) {
/* insert code to sync to interrupts here */
if (!vbl_status) {
when -= si->blank_period - 4;
}
/* do the things we do when we notice a vertical retrace */
result = thread_interrupt_work(flags, regs, si);
}
/* pick the "other" timer */
to = (timer *)&(di->ti_a);
if (to == te) to = (timer *)&(di->ti_b);
/* our guess as to when we should be back */
((timer_info *)to)->when_target = now + when;
/* reschedule the interrupt */
add_timer(to, timer_interrupt_func, ((timer_info *)to)->when_target, B_ONE_SHOT_ABSOLUTE_TIMER);
/* remember the currently active timer */
di->current_timer = (timer_info *)to;
}
return result;
}
#else
static int32
fake_interrupt_thread_func(void *_di)
{
device_info *di; /* = (device_info *)_di; */
shared_info *si; /* = di->si; */
int32 *flags; /* = &(si->flags); */
vuint32 *regs; /* = di->regs; */
bigtime_t last_sync;
bigtime_t this_sync;
bigtime_t diff_sync;
uint32 counter = 1;
ddprintf((" Device info points to %08x\n",(device_info *)_di));
di = (device_info *)_di;
ddprintf((" Shared info points to %08x\n",di->si));
si = di->si;
ddprintf((" Flags points to %08x\n",&(si->flags)));
flags = &(si->flags);
ddprintf((" Regs points to %08x\n",di->regs));
regs = di->regs;
/* a lie, but we have to start somewhen */
last_sync = system_time() - 8333;
ddprintf(("fake_interrupt_thread_func begins\ndi: 0x%08lx\nsi: 0x%08lx\nflags: 0x%08lx\n", (uint32)di, (uint32)si, (uint32)flags));
/* loop until notified */
while(atomic_and(flags, -1) & SKD_HANDLER_INSTALLED) {
/* see if "interrupts" are enabled */
if((volatile int32)(di->can_interrupt)) {
/* poll the retrace flag until set */
/* YOUR CODE HERE */
/* get the system_time */
this_sync = system_time();
/* do our stuff */
thread_interrupt_work(flags, regs, si);
} else {
/* get the system_time */
this_sync = system_time();
}
/* find out how long it took */
diff_sync = this_sync - last_sync;
/* back off a little so we're sure to catch the retrace */
diff_sync -= diff_sync / 10;
/*
impose some limits so we can recover from refresh rate changes
Supported refresh rates are 48 Hz - 120 Hz, so these limits should
be slightly wider.
*/
if(diff_sync < 8000) {
diff_sync = 8000; /* not less than 1/125th of sec */
}
if(diff_sync > 16666) {
diff_sync = 20000; /* not more than 1/40th of sec */
}
if((counter++ & 0x01ff) == 0) {
diff_sync >>= 2; /* periodically quarter the wait to resync */
}
/* update for next go-around */
last_sync = this_sync;
/* snooze until our next retrace */
snooze_until(this_sync + diff_sync, B_SYSTEM_TIMEBASE);
}
ddprintf(("fake_interrupt_thread_func ends with flags = 0x%08lx\n", *flags));
/* gotta return something */
return B_OK;
}
#endif
#endif
#if DEBUG > 0
static int dump(int argc, char **argv) {
int i;
kprintf("ATIMach64 Kernel Driver Persistant Data\n\nThere are %ld card(s)\n", pd->count);
kprintf("Driver wide benahpore: %ld/%ld\n", pd->kernel.ben, pd->kernel.sem);
kprintf("Total seen interrupts: %ld\n", pd->total_interrupts);
for (i = 0; i < pd->count; i++) {
device_info *di = &(pd->di[i]);
uint16 device_id = di->pcii.device_id;
shared_info *si = di->si;
kprintf(" device_id: 0x%04x\n", device_id);
kprintf(" interrupt count: %ld\n", di->interrupt_count);
if (si) {
kprintf(" cursor: %d,%d\n", si->cursor.x, si->cursor.y);
kprintf(" flags:");
if (si->flags & SKD_MOVE_CURSOR) kprintf(" SKD_MOVE_CURSOR");
if (si->flags & SKD_PROGRAM_CLUT) kprintf(" SKD_PROGRAM_CLUT");
if (si->flags & SKD_SET_START_ADDR) kprintf(" SKD_SET_START_ADDR");
kprintf(" vblank semaphore id: %ld\n", si->vblank);
}
}
return 1; /* the magic number for success */
}
#endif
static status_t open_hook (const char* name, uint32 flags, void** cookie) {
int32 index = 0;
device_info *di;
shared_info *si;
#if SEMAPHORE
thread_id thid;
thread_info thinfo;
#endif
status_t result = B_OK;
vuint32 *regs;
char shared_name[B_OS_NAME_LENGTH];
ddprintf(("SKD open_hook(%s, %ld, 0x%08lx)\n", name, flags, (uint32)cookie));
/* find the device name in the list of devices */
/* we're never passed a name we didn't publish */
while (pd->device_names[index] && (strcmp(name, pd->device_names[index]) != 0)) index++;
/* for convienience */
di = &(pd->di[index]);
/* make sure no one else has write access to the common data */
AQUIRE_BEN(pd->kernel);
/* if it's already open for writing */
if (di->is_open) {
/* mark it open another time */
goto mark_as_open;
}
/* create the shared area */
sprintf(shared_name, "%04X_%04X_%02X%02X%02X shared",
di->pcii.vendor_id, di->pcii.device_id,
di->pcii.bus, di->pcii.device, di->pcii.function);
/* create this area with NO user-space read or write permissions, to prevent accidental dammage */
di->shared_area = create_area(shared_name, (void **)&(di->si), B_ANY_KERNEL_ADDRESS, ((sizeof(shared_info) + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1)), B_FULL_LOCK, 0);
if (di->shared_area < 0) {
/* return the error */
result = di->shared_area;
goto done;
}
/* save a few dereferences */
si = di->si;
/* save the vendor and device IDs */
si->vendor_id = di->pcii.vendor_id;
si->device_id = di->pcii.device_id;
si->revision = di->pcii.revision;
/* map the device */
result = map_device(di);
if (result < 0) goto free_shared;
/* Find registers in memory */
/* This is the place for 2MB and 8MB boards */
si->regs = (void *)((unsigned int)si->framebuffer + 0x7ffc00);
if ((unsigned int)si->device_id != (inw(CONFIG_CHIP_ID) & CFG_CHIP_TYPE)) {
/* Try place for 4MB */
si->regs = (void *)((unsigned int)si->framebuffer + 0x3ffc00);
if (si->device_id != (inw(CONFIG_CHIP_ID) & CFG_CHIP_TYPE)) {
ddprintf(("Registers not found ! \n"));
goto free_shared;
}
}
/* obtain all information related to the Mach64 */
result = GetATIInfo(si);
if (result < 0) goto free_shared;
/* create a semaphore for vertical blank management */
#if SEMAPHORE
si->vblank = create_sem(0, di->name);
if (si->vblank < 0) {
result = si->vblank;
goto unmap;
}
#else
/* No semafore for the moment */
si->vblank = B_ERROR;
#endif
/* change the owner of the semaphores to the opener's team */
/* this is required because apps can't aquire kernel semaphores */
#if SEMAPHORE
thid = find_thread(NULL);
get_thread_info(thid, &thinfo);
set_sem_owner(si->vblank, thinfo.team);
#endif
/* assign local regs pointer for ATIMACH64xx() macros */
regs = di->regs;
/* disable and clear any pending interrupts */
#if SEMAPHORE
/* if we're faking interrupts */
if ((di->pcii.u.h0.interrupt_pin == 0x00) || (di->pcii.u.h0.interrupt_line == 0xff)){
/* fake some kind of interrupt with a timer */
di->can_interrupt = FALSE;
si->flags = SKD_HANDLER_INSTALLED;
si->refresh_period = 16666; /* fake 60Hz to start */
si->blank_period = si->refresh_period / 20;
#if defined(POST_R4_0)
di->ti_a.di = di; /* refer to ourself */
di->ti_b.di = di;
di->current_timer = &(di->ti_a);
/* program the first timer interrupt, and it will handle the rest */
result = add_timer((timer *)(di->current_timer), timer_interrupt_func, si->refresh_period, B_ONE_SHOT_RELATIVE_TIMER);
/* bail if we can't add the timer */
if (result != B_OK) goto delete_the_sem;
#else */
/* fake some kind of interrupt with a thread */
result = di->tid = spawn_kernel_thread(fake_interrupt_thread_func, "SKD fake interrupt", B_REAL_TIME_DISPLAY_PRIORITY, di);
/* bail if we can't spawn the thread */
if(result < 0) goto delete_the_sem;
/* start up the thread */
resume_thread(di->tid);
#endif
} else {
/* otherwise install our interrupt handler */
result = install_io_interrupt_handler(di->pcii.u.h0.interrupt_line, atimach64_interrupt, (void *)di, 0);
/* bail if we couldn't install the handler */
if (result != B_OK) goto delete_the_sem;
}
#endif
mark_as_open:
/* mark the device open */
di->is_open++;
/* send the cookie to the opener */
*cookie = di;
goto done;
/* delete_the_sem: */
delete_sem(si->vblank);
/* unmap: */
unmap_device(di);
free_shared:
/* clean up our shared area */
delete_area(di->shared_area);
di->shared_area = -1;
di->si = NULL;
done:
/* end of critical section */
RELEASE_BEN(pd->kernel);
/* all done, return the status */
ddprintf(("open_hook returning 0x%08lx\n", result));
return result;
}
/* ----------
read_hook - does nothing, gracefully
----- */
static status_t
read_hook (void* dev, off_t pos, void* buf, size_t* len)
{
*len = 0;
return B_NOT_ALLOWED;
}
/* ----------
write_hook - does nothing, gracefully
----- */
static status_t
write_hook (void* dev, off_t pos, const void* buf, size_t* len)
{
*len = 0;
return B_NOT_ALLOWED;
}
/* ----------
close_hook - does nothing, gracefully
----- */
static status_t
close_hook (void* dev)
{
ddprintf(("SKD close_hook(%08lx)\n", (uint32)dev));
/* we don't do anything on close: there might be dup'd fd */
return B_NO_ERROR;
}
/* -----------
free_hook - close down the device
----------- */
static status_t
free_hook (void* dev) {
device_info *di = (device_info *)dev;
shared_info *si = di->si;
ddprintf(("SKD free_hook() begins...\n"));
/* lock the driver */
AQUIRE_BEN(pd->kernel);
/* if opened multiple times, decrement the open count and exit */
if (di->is_open > 1)
goto unlock_and_exit;
#if SEMAPHORE
/* disable and clear any pending interrupts */
/* if we were faking the interrupts */
if ((di->pcii.u.h0.interrupt_pin == 0x00) || (di->pcii.u.h0.interrupt_line == 0xff)){
/* stop our interrupt faking thread */
si->flags = 0;
di->can_interrupt = FALSE;
#if defined(POST_R4_0)
/* cancel the timer */
/* we don't know which one is current, so cancel them both and ignore any error */
cancel_timer((timer *)&(di->ti_a));
cancel_timer((timer *)&(di->ti_b));
#else
/* we don't do anything here, as the R4 kernel reaps its own threads */
/* After R4.0 we can do it ourselves, but we'd rather use timers */
#endif
/* otherwise */
} else {
/* remove interrupt handler */
remove_io_interrupt_handler(di->pcii.u.h0.interrupt_line, atimach64_interrupt, di);
}
/* delete the semaphores, ignoring any errors ('cause the owning team may have died on us) */
delete_sem(si->vblank);
#endif
si->vblank = -1;
/* free regs and framebuffer areas */
unmap_device(di);
/* clean up our shared area */
delete_area(di->shared_area);
di->shared_area = -1;
di->si = NULL;
unlock_and_exit:
/* mark the device available */
di->is_open--;
/* unlock the driver */
RELEASE_BEN(pd->kernel);
ddprintf(("SKD free_hook() ends.\n"));
/* all done */
return B_OK;
}
/* -----------
control_hook - where the real work is done
----------- */
static status_t
control_hook (void* dev, uint32 msg, void *buf, size_t len) {
device_info *di = (device_info *)dev;
status_t result = B_DEV_INVALID_IOCTL;
/* ddprintf(("ioctl: %d, buf: 0x%08x, len: %d\n", msg, buf, len));*/
switch (msg) {
/* the only PUBLIC ioctl */
case B_GET_ACCELERANT_SIGNATURE: {
char *sig = (char *)buf;
strcpy(sig, "atimach64vt.accelerant");
result = B_OK;
} break;
/* PRIVATE ioctl from here on */
case ATIMACH64_GET_PRIVATE_DATA: {
atimach64_get_private_data *gpd = (atimach64_get_private_data *)buf;
if (gpd->magic == ATIMACH64_PRIVATE_DATA_MAGIC) {
gpd->shared_info_area = di->shared_area;
result = B_OK;
}
} break;
case ATIMACH64_GET_PCI: {
atimach64_get_set_pci *gsp = (atimach64_get_set_pci *)buf;
if (gsp->magic == ATIMACH64_PRIVATE_DATA_MAGIC) {
pci_info *pcii = &(di->pcii);
gsp->value = get_pci(gsp->offset, gsp->size);
result = B_OK;
}
} break;
case ATIMACH64_SET_PCI: {
atimach64_get_set_pci *gsp = (atimach64_get_set_pci *)buf;
if (gsp->magic == ATIMACH64_PRIVATE_DATA_MAGIC) {
pci_info *pcii = &(di->pcii);
set_pci(gsp->offset, gsp->size, gsp->value);
result = B_OK;
}
} break;
case ATIMACH64_RUN_INTERRUPTS: {
atimach64_set_bool_state *ri = (atimach64_set_bool_state *)buf;
if (ri->magic == ATIMACH64_PRIVATE_DATA_MAGIC) {
/* are we faking interrupts? */
if ((di->pcii.u.h0.interrupt_pin == 0x00) || (di->pcii.u.h0.interrupt_line == 0xff)){
di->can_interrupt = ri->do_it;
} else {
vuint32 *regs = di->regs;
if (ri->do_it) {
/* resume interrupts */
*regs = *regs; /* CHANGE ME */
} else {
/* disable interrupts */
*regs = *regs; /* CHANGE ME */
}
}
result = B_OK;
}
} break;
}
return result;
}
@@ -0,0 +1,256 @@
/* All this code belongs to Linux Xfree86 Project with lots of modifications */
/*
Rene MacKinney <rene_@freenet.co.uk>
7.May.99
*/
#include <KernelExport.h>
#include "DriverInterface.h"
#include "regmach64.h"
#include "string.h"
#if DEBUG > 0
#define ddprintf(a) dprintf a
#else
#define ddprintf(a)
#endif
status_t
GetATIInfo(si)
shared_info *si;
{
char signature[] = " 761295520";
char *bios_data = rommem(0x00);
char bios_signature[10];
unsigned short *sbios_data = (unsigned short *)bios_data;
int tmp,i,j;
int ROM_Table_Offset;
int Freq_Table_Ptr;
int CDepth_Table_Ptr;
int CTable_Size;
if (si->rom == NULL)
return B_ERROR;
memcpy(bios_signature, rommem(0x30), 10);
if (strncmp( signature, bios_signature, 10 )) {
ddprintf(("Mach64 probe failed on BIOS signature\n"));
return B_ERROR;
}
si->board_identifier[0] = bios_data[ 0x40 ];
si->board_identifier[1] = bios_data[ 0x41 ];
si->equipment_flags[0] = bios_data[ 0x42 ];
si->equipment_flags[1] = bios_data[ 0x44 ];
si->asic_identifier = bios_data[ 0x43 ];
si->bios_major = bios_data[ 0x4c ];
si->bios_minor = bios_data[ 0x4d ];
strncpy( si->bios_date, bios_data + 0x50, 20 );
si->VGA_Wonder_Present = bios_data[ 0x44 ] & 0x40;
si->Mach64_Present = 1; /* Test for Mach64 product */
tmp = inw(SCRATCH_REG0);
outw(SCRATCH_REG0, 0x55555555);
if (inw(SCRATCH_REG0) != 0x55555555) {
si->Mach64_Present = 0;
ddprintf(("Mach64 probe failed on read 1 of SCRATCH_REG0 %x\n",
SCRATCH_REG0));
} else {
outw(SCRATCH_REG0, 0xaaaaaaaa);
if (inw(SCRATCH_REG0) != 0xaaaaaaaa) {
si->Mach64_Present = 0;
ddprintf(("Mach64 probe failed on read 2 of SCRATCH_REG0 %x\n",
SCRATCH_REG0));
}
}
outw(SCRATCH_REG0, tmp);
if (!si->Mach64_Present)
return B_ERROR;
tmp = inw(CONFIG_CHIP_ID);
if (si->device_id != (tmp & CFG_CHIP_TYPE)) {
ddprintf(("%x %x: Mach64 chipset mismatch",
si->device_id, tmp & CFG_CHIP_TYPE));
return(B_ERROR);
}
ddprintf(("CONFIG_CHIP_ID reports: %x rev %d\n",
si->device_id, si->revision));
tmp = inw(CONFIG_STAT0);
if (si->device_id == MACH64_GX_ID || si->device_id == MACH64_CX_ID) {
si->Bus_Type = tmp & CFG_BUS_TYPE;
si->Mem_Type = (tmp & CFG_MEM_TYPE) >> 3;
si->DAC_Type = (tmp & CFG_INIT_DAC_TYPE) >> 9;
si->DAC_SubType = (inb(SCRATCH_REG1+1) & 0xf0) | si->DAC_Type;
} else {
si->Mem_Type = tmp & CFG_MEM_TYPE_xT;
si->DAC_Type = DAC_INTERNAL;
si->DAC_SubType = DAC_INTERNAL;
ddprintf(("CONFIG_STAT0 reports mem type %d\n", si->Mem_Type));
ddprintf(("CONFIG_STAT0 is 0x%08x\n", tmp));
}
/* The copy of the registers starts at 0x7ffc00 or 0x3ffc00 depending on the
memory on the card */
tmp = inw(MEM_CNTL);
if (si->device_id == MACH64_GX_ID ||
si->device_id == MACH64_CX_ID ||
si->device_id == MACH64_CT_ID ||
si->device_id == MACH64_ET_ID ||
((si->device_id == MACH64_VT_ID || si->device_id == MACH64_GT_ID) &&
!(si->revision & 0x07))) {
switch (tmp & MEM_SIZE_ALIAS) {
case MEM_SIZE_512K:
si->mem_size = 512 * 1024;
break;
case MEM_SIZE_1M:
si->mem_size = 1024 * 1024;
break;
case MEM_SIZE_2M:
si->mem_size = 2*1024 * 1024;
break;
case MEM_SIZE_4M:
si->mem_size = 4*1024 * 1024;
break;
case MEM_SIZE_6M:
si->mem_size = 6*1024 * 1024;
break;
case MEM_SIZE_8M:
si->mem_size = 8*1024 * 1024;
break;
}
si->Mem_Size = tmp & MEM_SIZE_ALIAS;
} else {
switch (tmp & MEM_SIZE_ALIAS_GTB) {
case MEM_SIZE_512K:
si->mem_size = 512 * 1024;
break;
case MEM_SIZE_1M:
si->mem_size = 1024 * 1024;
break;
case MEM_SIZE_2M_GTB:
si->mem_size = 2*1024 * 1024;
break;
case MEM_SIZE_4M_GTB:
si->mem_size = 4*1024 * 1024;
break;
case MEM_SIZE_6M_GTB:
si->mem_size = 6*1024 * 1024;
break;
case MEM_SIZE_8M_GTB:
si->mem_size = 8*1024 * 1024;
break;
case MEM_SIZE_16M_GTB:
si->mem_size = 16*1024 * 1024;
break;
}
si->Mem_Size = tmp & MEM_SIZE_ALIAS_GTB;
}
ddprintf(("Card Reports 0x%08x Memory\n", si->mem_size));
ROM_Table_Offset = sbios_data[0x48 >> 1];
Freq_Table_Ptr = sbios_data[(ROM_Table_Offset >> 1) + 8];
si->Clock_Type = bios_data[Freq_Table_Ptr];
si->MinFreq = sbios_data[(Freq_Table_Ptr >> 1) + 1];
si->MaxFreq = sbios_data[(Freq_Table_Ptr >> 1) + 2];
si->RefFreq = sbios_data[(Freq_Table_Ptr >> 1) + 4];
si->RefDivider = sbios_data[(Freq_Table_Ptr >> 1) + 5];
si->NAdj = sbios_data[(Freq_Table_Ptr >> 1) + 6];
si->DRAMMemClk = sbios_data[(Freq_Table_Ptr >> 1) + 8];
si->VRAMMemClk = sbios_data[(Freq_Table_Ptr >> 1) + 9];
si->MemClk = bios_data[Freq_Table_Ptr + 22];
si->CXClk = bios_data[Freq_Table_Ptr + 6];
CDepth_Table_Ptr = sbios_data[(Freq_Table_Ptr >> 1) - 3];
Freq_Table_Ptr = sbios_data[(Freq_Table_Ptr >> 1) - 1];
for (i = 0; i < MACH64_NUM_CLOCKS; i++)
si->Clocks[i] = sbios_data[(Freq_Table_Ptr >> 1) + i];
si->MemCycle = bios_data[ROM_Table_Offset + 0];
CTable_Size = bios_data[CDepth_Table_Ptr - 1];
for (i = 0, j = 0;
bios_data[CDepth_Table_Ptr + i] != 0;
i += CTable_Size, j++) {
si->Freq_Table[j].h_disp = bios_data[CDepth_Table_Ptr + i];
si->Freq_Table[j].dacmask = bios_data[CDepth_Table_Ptr + i + 1];
si->Freq_Table[j].ram_req = bios_data[CDepth_Table_Ptr + i + 2];
si->Freq_Table[j].max_dot_clock = bios_data[CDepth_Table_Ptr + i + 3];
si->Freq_Table[j].color_depth = bios_data[CDepth_Table_Ptr + i + 4];
switch(si->Freq_Table[j].color_depth) {
case 2: /* 8 Bit */
si->pix_clk_max8 = max(si->pix_clk_max8, si->Freq_Table[j].max_dot_clock);
break;
case 4: /* 16 Bit */
si->pix_clk_max16 = max(si->pix_clk_max16, si->Freq_Table[j].max_dot_clock);
break;
case 6: /* 8 Bit */
si->pix_clk_max32 = max(si->pix_clk_max32, si->Freq_Table2[j].max_dot_clock);
break;
}
}
si->Freq_Table[j].h_disp = 0;
if (bios_data[CDepth_Table_Ptr + i + 1] != 0) {
CDepth_Table_Ptr += i + 2;
CTable_Size = bios_data[CDepth_Table_Ptr - 1];
for (i = 0, j = 0;
bios_data[CDepth_Table_Ptr + i] != 0;
i += CTable_Size, j++) {
si->Freq_Table2[j].h_disp = bios_data[CDepth_Table_Ptr + i];
si->Freq_Table2[j].dacmask = bios_data[CDepth_Table_Ptr + i +
1];
si->Freq_Table2[j].ram_req = bios_data[CDepth_Table_Ptr + i +
2];
si->Freq_Table2[j].max_dot_clock = bios_data[CDepth_Table_Ptr + i +
3];
si->Freq_Table2[j].color_depth = bios_data[CDepth_Table_Ptr + i +
4];
switch(si->Freq_Table[j].color_depth) {
case 2: /* 8 Bit */
si->pix_clk_max8 = max(si->pix_clk_max8, si->Freq_Table2[j].max_dot_clock);
break;
case 4: /* 16 Bit */
si->pix_clk_max16 = max(si->pix_clk_max16, si->Freq_Table2[j].max_dot_clock);
break;
case 6: /* 8 Bit */
si->pix_clk_max32 = max(si->pix_clk_max32, si->Freq_Table2[j].max_dot_clock);
break;
}
}
si->Freq_Table2[j].h_disp = 0;
} else
si->Freq_Table2[0].h_disp = 0;
/* Hardwired 120000 for 8bpp and 80000 for the rest */
ddprintf(("Clocks %ld %ld %ld\n",si->pix_clk_max32,
si->pix_clk_max16, si->pix_clk_max8));
si->pix_clk_max32 = 80000;
si->pix_clk_max16 = 80000;
si->pix_clk_max8 = 120000;
ddprintf(("Clocks %ld %ld %ld\n",si->pix_clk_max32,
si->pix_clk_max16, si->pix_clk_max8));
return B_OK;
}