diff --git a/headers/private/graphics/s3savage/DriverInterface.h b/headers/private/graphics/s3savage/DriverInterface.h new file mode 100644 index 0000000000..16933cb87a --- /dev/null +++ b/headers/private/graphics/s3savage/DriverInterface.h @@ -0,0 +1,197 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#ifndef DRIVERINTERFACE_H +#define DRIVERINTERFACE_H + +#include +#include +#include +#include + +/* + This is the info that needs to be shared between the kernel driver and + the accelerant for the sample driver. +*/ +#if defined(__cplusplus) +extern "C" { +#endif + +typedef struct +{ + sem_id sem; + int32 ben; +} benaphore; + +#define INIT_BEN(x) x.sem = create_sem(0, "SAVAGE "#x" benaphore"); x.ben = 0; +#define AQUIRE_BEN(x) if((atomic_add(&(x.ben), 1)) >= 1) acquire_sem(x.sem); +#define RELEASE_BEN(x) if((atomic_add(&(x.ben), -1)) > 1) release_sem(x.sem); +#define DELETE_BEN(x) delete_sem(x.sem); + + +#define SAVAGE_PRIVATE_DATA_MAGIC 0x1234 /* a private driver rev, of sorts */ + +#define MAX_SAVAGE_DEVICE_NAME_LENGTH 32 + +#define SKD_MOVE_CURSOR 0x00000001 +#define SKD_PROGRAM_CLUT 0x00000002 +#define SKD_SET_START_ADDR 0x00000004 +#define SKD_SET_CURSOR 0x00000008 +#define SKD_HANDLER_INSTALLED 0x80000000 + +enum +{ + SAVAGE_GET_PRIVATE_DATA = B_DEVICE_OP_CODES_END + 1, + SAVAGE_GET_PCI, + SAVAGE_SET_PCI, + SAVAGE_DEVICE_NAME, + SAVAGE_RUN_INTERRUPTS, + SAVAGE_DPRINTF +}; + +typedef struct +{ + uint16 vendor_id; /* PCI vendor ID, from pci_info */ + uint16 device_id; /* PCI device ID, from pci_info */ + uint8 revision; /* PCI device revsion, from pci_info */ + area_id regs_area; /* Kernel's area_id for the memory mapped registers. + It will be cloned into the accelerant's address + space. */ + area_id fb_area; /* Frame buffer's area_id. The addresses are shared + with all teams. */ + void *framebuffer; /* As viewed from virtual memory */ + void *framebuffer_pci; /* As viewed from the PCI bus (for DMA) */ + area_id rom_area; /* Mapped ROM's area_id */ + void *rom; /* As viewed from virtual memory. Shared by all teams */ + area_id mode_area; /* Contains the list of display modes the driver supports */ + uint32 mode_count; /* Number of display modes in the list */ + sem_id vblank; /* The vertical blank semaphore. Ownership will be + transfered to the team opening the device first */ + int32 flags; + int32 start_addr; + + struct + { + uint8* data; /* Pointer into the frame buffer to where the + cursor data starts */ + uint16 hot_x; /* Cursor hot spot. The top left corner of the cursor */ + uint16 hot_y; /* is 0,0 */ + uint16 x; /* The location of the cursor hot spot on the */ + uint16 y; /* display (or desktop?) */ + uint16 width; /* Width and height of the cursor shape */ + uint16 height; + bool is_visible; /* Is the cursor currently displayed? */ + } cursor; + uint16 first_color; + uint16 color_count; + bigtime_t refresh_period; /* Duration of one frame (ie 1/refresh rate) */ + bigtime_t blank_period; /* Duration of the blanking period. These are + usefull when faking vertical blanking + interrupts. */ + uint8 color_data[3 * 256]; /* */ + uint8 cursor0[64*64/8]; /* AND mask for a 64x64 cursor */ + uint8 cursor1[512]; /* XOR mask for a 64x64 cursor */ + display_mode dm; /* current display mode configuration */ + frame_buffer_config + fbc; /* bytes_per_row and start of frame buffer */ + struct + { + uint64 count; /* last fifo slot used */ + uint64 last_idle; /* last fifo slot we *know* the engine was idle after */ + benaphore lock; /* for serializing access to the acceleration engine */ + } engine; + + uint32 pix_clk_max8; /* The maximum speed the pixel clock should run */ + uint32 pix_clk_max16; /* at for a given pixel width. Usually a function */ + uint32 pix_clk_max32; /* of memory and DAC bandwidths. */ + uint32 mem_size; /* Frame buffer memory, in bytes. */ +} shared_info; + +/* Read or write a value in PCI configuration space */ +typedef struct +{ + uint32 magic; /* magic number to make sure the caller groks us */ + uint32 offset; /* Offset to read/write */ + uint32 size; /* Number of bytes to transfer */ + uint32 value; /* The value read or written */ +} savage_get_set_pci; + +/* Set some boolean condition (like enabling or disabling interrupts) */ +typedef struct +{ + uint32 magic; /* magic number to make sure the caller groks us */ + bool do_it; /* state to set */ +} savage_set_bool_state; + +/* Retrieve the area_id of the kernel/accelerant shared info */ +typedef struct +{ + uint32 magic; /* magic number to make sure the caller groks us */ + area_id shared_info_area; /* area_id containing the shared information */ +} savage_get_private_data; + +/* Retrieve the device name. Usefull for when we have a file handle, but want +to know the device name (like when we are cloning the accelerant) */ +typedef struct +{ + uint32 magic; /* magic number to make sure the caller groks us */ + char *name; /* The name of the device, less the /dev root */ +} savage_device_name; + +enum +{ + SAVAGE_WAIT_FOR_VBLANK = (0 << 0) +}; + +enum +{ + /* Savage3D series */ + PCI_PID_SAVAGE3D = 0x8a20, /* Savage3D */ + PCI_PID_SAVAGE3DMV = 0x8a21, /* Savage3D/MV */ + PCI_PID_SAVAGEMXMV = 0x8c10, /* Savage/MX-MV */ + PCI_PID_SAVAGEMX = 0x8c11, /* Savage/MX */ + PCI_PID_SAVAGEIXMV = 0x8c12, /* Savage/IX-MV */ + PCI_PID_SAVAGEIX = 0x8c13, /* Savage/IX */ + + /* Savage4 series */ + PCI_PID_SAVAGE4_2 = 0x8a22, /* Savage4 */ + PCI_PID_SAVAGE4_3 = 0x8a23, /* Savage4 */ + PCI_PID_SAVAGE2000 = 0x9102, /* Savage2000 */ + PCI_PID_PM133 = 0x8a25, /* ProSavage PM133 */ + PCI_PID_KM133 = 0x8a26, /* ProSavage KM133 */ + PCI_PID_PN133 = 0x8d01, /* ProSavage PN133, 86C380 [ProSavageDDR K4M266] */ + PCI_PID_KN133 = 0x8d02, /* ProSavage KN133/TwisterK AGP4X VT8636A */ + PCI_PID_KM266 = 0x8d04, /* ProSavage8 KM266/KL266 VT8375 */ + PCI_PID_PN266 = 0x8d03, /* VT8751 [ProSavageDDR P4M266] */ + + /* SuperSavage series (unsupported) */ + PCI_PID_SUPERSAVAGE_MX128 = 0x8c22, /* SuperSavage MX/128 */ + PCI_PID_SUPERSAVAGE_MX64 = 0x8c24, /* SuperSavage MX/64 */ + PCI_PID_SUPERSAVAGE_MX64C = 0x8c26, /* SuperSavage MX/64C */ + PCI_PID_SUPERSAVAGE_IX128_SDR = 0x8c2a, /* SuperSavage IX/128 SDR */ + PCI_PID_SUPERSAVAGE_IX128_DDR = 0x8c2b, /* SuperSavage IX/128 DDR */ + PCI_PID_SUPERSAVAGE_IX64_SDR = 0x8c2c, /* SuperSavage IX/64 SDR */ + PCI_PID_SUPERSAVAGE_IX64_DDR = 0x8c2d, /* SuperSavage IX/64 DDR */ + PCI_PID_SUPERSAVAGE_IXC_SDR = 0x8c2e, /* SuperSavage IX/C SDR */ + PCI_PID_SUPERSAVAGE_IXC_DDR = 0x8c2f, /* SuperSavage IX/C DDR */ +}; + +#define isSavage4Family(p) \ + ((p) == PCI_PID_SAVAGE4_2 || \ + (p) == PCI_PID_SAVAGE4_3 || \ + (p) == PCI_PID_SAVAGE2000 || \ + (p) == PCI_PID_PM133 || \ + (p) == PCI_PID_KM133 || \ + (p) == PCI_PID_PN133 || \ + (p) == PCI_PID_KN133 || \ + (p) == PCI_PID_KM266) + + +#if defined(__cplusplus) +} +#endif + + +#endif diff --git a/src/add-ons/accelerants/Jamfile b/src/add-ons/accelerants/Jamfile index 923283b154..7aef3e3b59 100644 --- a/src/add-ons/accelerants/Jamfile +++ b/src/add-ons/accelerants/Jamfile @@ -7,6 +7,7 @@ SubInclude HAIKU_TOP src add-ons accelerants matrox ; SubInclude HAIKU_TOP src add-ons accelerants neomagic ; SubInclude HAIKU_TOP src add-ons accelerants nvidia ; SubInclude HAIKU_TOP src add-ons accelerants radeon ; +SubInclude HAIKU_TOP src add-ons accelerants s3savage ; SubInclude HAIKU_TOP src add-ons accelerants tdfx ; SubInclude HAIKU_TOP src add-ons accelerants skeleton ; SubInclude HAIKU_TOP src add-ons accelerants vesa ; diff --git a/src/add-ons/accelerants/s3savage/Acceleration.c b/src/add-ons/accelerants/s3savage/Acceleration.c new file mode 100644 index 0000000000..14f86a297a --- /dev/null +++ b/src/add-ons/accelerants/s3savage/Acceleration.c @@ -0,0 +1,121 @@ +/* + * Copyright 1999 Erdi Chen + */ + +#include "GlobalData.h" +#include "generic.h" +#include "s3accel.h" +#include "s3mmio.h" + +void +SCREEN_TO_SCREEN_BLIT ( + engine_token *et, blit_params *list, uint32 count) +{ + s3accel_wait_for_fifo (1); + UpdateGEReg16 (FRGD_MIX, 0x0067); + + while (count--) + { + int cmd = 0xc0b1; + int src_x = list->src_left; + int src_y = list->src_top; + int dest_x = list->dest_left; + int dest_y = list->dest_top; + int width = list->width; + int height = list->height; + + if (src_x < dest_x) + { + src_x += width; dest_x += width; + cmd ^=32; + } + + if (src_y < dest_y) + { + src_y += height; dest_y += height; + cmd ^=128; + } + + UpdateGEReg (ALT_CURXY, (src_x<<16)|src_y); + UpdateGEReg (ALT_STEP, (dest_x<<16)|dest_y); + UpdateGEReg (ALT_PCNT, (width<<16)|height); + UpdateGEReg (CMD, cmd); + + list ++; + } + + s3accel_wait_for_fifo (3); + UpdateGEReg16 (FRGD_MIX, 0x0027); +} + +void +FILL_RECTANGLE ( + engine_token *et, uint32 color, fill_rect_params *list, uint32 count) +{ + s3accel_wait_for_fifo (1); + UpdateGEReg (FRGD_COLOR, color); + UpdateGEReg16 (FRGD_MIX, 0x0027); + + while (count--) + { + int x = list->left; + int y = list->top; + int w = list->right - x; + int h = list->bottom - y; + + s3accel_wait_for_fifo (2); + UpdateGEReg (ALT_CURXY, (x<<16)|y); + UpdateGEReg (ALT_PCNT, (w<<16)|h); + UpdateGEReg (CMD, 0x000040b1); + + list++; + } + s3accel_wait_for_fifo(3); +} + +void +INVERT_RECTANGLE ( + engine_token *et, fill_rect_params *list, uint32 count) +{ + s3accel_wait_for_fifo (1); + UpdateGEReg16 (FRGD_MIX, 0x0020); + + while (count--) + { + int x = list->left; + int y = list->top; + int w = list->right - x; + int h = list->bottom - y; + + s3accel_wait_for_fifo (2); + UpdateGEReg (ALT_CURXY, (x<<16)|y); + UpdateGEReg (ALT_PCNT, (w<<16)|h); + UpdateGEReg (CMD, 0x000040b1); + + list++; + } + + s3accel_wait_for_fifo (3); + UpdateGEReg16 (FRGD_MIX, 0x0027); +} + +void +FILL_SPAN ( + engine_token *et, uint32 color, uint16 *list, uint32 count) +{ + s3accel_wait_for_fifo (1); + UpdateGEReg (FRGD_COLOR, color); + UpdateGEReg16 (FRGD_MIX, 0x0027); + + while (count--) + { + int y = *list ++; + int x = *list ++; + int w = *list - x; list ++; + + s3accel_wait_for_fifo (2); + UpdateGEReg (ALT_CURXY, (x<<16)|y); + UpdateGEReg (ALT_PCNT, (w<<16)|1); + UpdateGEReg (CMD, 0x000040b1); + } +} diff --git a/src/add-ons/accelerants/s3savage/Cursor.c b/src/add-ons/accelerants/s3savage/Cursor.c new file mode 100644 index 0000000000..c21af8e985 --- /dev/null +++ b/src/add-ons/accelerants/s3savage/Cursor.c @@ -0,0 +1,113 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +/* + * Copyright 1999 Erdi Chen + */ + +#include "GlobalData.h" +#include "generic.h" +#include "s3drv.h" + +void set_cursor_colors(void) +{ + /* it's only called by the INIT_ACCELERANT() */ + s3drv_set_cursor_color (0, ~0); +} + +status_t +SET_CURSOR_SHAPE ( + uint16 width, uint16 height, uint16 hot_x, uint16 hot_y, + uint8 *andMask, uint8 *xorMask) +{ +/* 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 + { + /* Update cursor variables appropriately. */ + si->cursor.width = width; + si->cursor.height = height; + si->cursor.hot_x = hot_x; + si->cursor.hot_y = hot_y; + s3drv_load_cursor ((byte_t*)framebuffer, 0, + width, height, andMask, xorMask); + } + + 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_p, uint16 y_p) +{ + int x = x_p, y = y_p; + bool move_screen = false; + /* the current horizontal starting pixel */ + uint16 hds = si->dm.h_display_start; + /* the current vertical starting line */ + uint16 vds = si->dm.v_display_start; + + /* Need to set this value for 32 bit */ + uint16 h_adjust = 3; + + /* 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; + x -= si->cursor.hot_x; + y -= si->cursor.hot_y; + + /* position the cursor on the display */ + s3drv_move_cursor (x, y); +} + +void SHOW_CURSOR (bool is_visible) +{ + if (is_visible) + s3drv_show_cursor (); + else + s3drv_hide_cursor (); + + /* record for our info */ + si->cursor.is_visible = is_visible; +} diff --git a/src/add-ons/accelerants/s3savage/EngineManagement.c b/src/add-ons/accelerants/s3savage/EngineManagement.c new file mode 100644 index 0000000000..d362be1211 --- /dev/null +++ b/src/add-ons/accelerants/s3savage/EngineManagement.c @@ -0,0 +1,70 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +/* + * Copyright 1999 Erdi Chen + */ + +#include "GlobalData.h" +#include "generic.h" +#include "s3drv.h" + + +static engine_token savage_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 = &savage_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) +{ + s3drv_wait_for_idle (); + // 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) +{ + s3drv_wait_for_idle (); + si->engine.last_idle = st->counter; + return B_OK; +} + diff --git a/src/add-ons/accelerants/s3savage/GetAccelerantHook.c b/src/add-ons/accelerants/s3savage/GetAccelerantHook.c new file mode 100644 index 0000000000..48d1833b1a --- /dev/null +++ b/src/add-ons/accelerants/s3savage/GetAccelerantHook.c @@ -0,0 +1,94 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +/* + * Copyright 1999 Erdi Chen + */ + +#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; +} diff --git a/src/add-ons/accelerants/s3savage/GetModeInfo.c b/src/add-ons/accelerants/s3savage/GetModeInfo.c new file mode 100644 index 0000000000..8374255a5d --- /dev/null +++ b/src/add-ons/accelerants/s3savage/GetModeInfo.c @@ -0,0 +1,96 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +/* + * Copyright 1999 Erdi Chen + */ + +#include "GlobalData.h" +#include "generic.h" + +/* + 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; + + /* max pixel clock is pixel depth dependant */ + switch (dm->space & ~0x3000) + { + case B_RGB32: clock_limit = si->pix_clk_max32; break; + case B_RGB15: + case B_RGB16: clock_limit = si->pix_clk_max16; break; + case B_CMAP8: clock_limit = si->pix_clk_max8; break; + default: + clock_limit = 0; + } + /* lower limit of about 48Hz vertical refresh */ + *low = (total_pix * 48L) / 1000L; + if (*low > clock_limit) return B_ERROR; + *high = clock_limit; + return B_OK; +} + +status_t GET_TIMING_CONSTRAINTS(display_timing_constraints *dtc) +{ + status_t retval; + + dtc->h_res = 8; + dtc->h_sync_min = 2*8; + dtc->h_sync_max = 32*8 - 1; + dtc->h_blank_min = 0; + dtc->h_blank_max = 64*8 - 1; + dtc->v_res = 1; + dtc->v_sync_min = 2; + dtc->v_sync_max = 15; + dtc->v_blank_min = 0; + dtc->v_blank_max = 255; + + retval = B_OK; + return retval; +} + +/* + 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; +} diff --git a/src/add-ons/accelerants/s3savage/GlobalData.c b/src/add-ons/accelerants/s3savage/GlobalData.c new file mode 100644 index 0000000000..138953bf93 --- /dev/null +++ b/src/add-ons/accelerants/s3savage/GlobalData.c @@ -0,0 +1,33 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +/* + * Copyright 1999 Erdi Chen + */ + +#include "GlobalData.h" +#include +#include +#include + +int fd; +shared_info *si; +area_id shared_info_area; +vuint32 *regs; +area_id regs_area; +vuint32 *framebuffer; +area_id fb_area; +display_mode *my_mode_list; +area_id my_mode_list_area; +int accelerantIsClone; + +void dpf (const char * format, ...) +{ + char buffer[4096] = "SAVAGE: "; + va_list args; + va_start (args, format); + vsprintf (buffer + 8, format, args); + ioctl (fd, SAVAGE_DPRINTF, buffer, 0); + va_end (args); +} diff --git a/src/add-ons/accelerants/s3savage/GlobalData.h b/src/add-ons/accelerants/s3savage/GlobalData.h new file mode 100644 index 0000000000..5301f73577 --- /dev/null +++ b/src/add-ons/accelerants/s3savage/GlobalData.h @@ -0,0 +1,25 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#if !defined(GLOBALDATA_H) +#define GLOBALDATA_H + +#include "DriverInterface.h" + +extern int fd; +extern shared_info *si; +extern area_id shared_info_area; +extern vuint32 *regs; +extern area_id regs_area; +extern vuint32 *framebuffer; +extern area_id fb_area; +extern display_mode *my_mode_list; +extern area_id my_mode_list_area; +extern int accelerantIsClone; + +/* Print debug message through kernel driver. Should move to other location later. */ +extern void dpf (const char * format, ...); + +#endif diff --git a/src/add-ons/accelerants/s3savage/InitAccelerant.c b/src/add-ons/accelerants/s3savage/InitAccelerant.c new file mode 100644 index 0000000000..81fe388a69 --- /dev/null +++ b/src/add-ons/accelerants/s3savage/InitAccelerant.c @@ -0,0 +1,494 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +/* + * Copyright 1999 Erdi Chen + */ + +#include "GlobalData.h" +#include "generic.h" +#include "s3drv.h" +#include "s3mmio.h" +#include "s3accel.h" + +#include "string.h" +#include "unistd.h" +#include "sys/types.h" +#include "sys/stat.h" +#include "fcntl.h" +#include + +/* defined in ProposeDisplayMode.c */ +extern status_t create_mode_list(void); +/* defined in Cursor.c */ +extern void set_cursor_colors(void); + +/* Get the linear address of an area. Workaround for BeOS bug that + returns invalid pointer from clone_area. */ +static void * __get_area_address (area_id area) +{ + area_info ai; + get_area_info (area, &ai); + return ai.address; +} + +// 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 *)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; + savage_get_private_data gpd; + + /* memorize the file descriptor */ + fd = the_fd; + dpf ("init_common begin\n"); + /* set the magic number so the driver knows we're for real */ + gpd.magic = SAVAGE_PRIVATE_DATA_MAGIC; + /* contact driver and get a pointer to the registers and shared data */ + result = ioctl(fd, SAVAGE_GET_PRIVATE_DATA, &gpd, sizeof(gpd)); + if (result != B_OK) goto error0; + + /* clone the shared area for our use */ + shared_info_area = clone_area ( + "SAVAGE 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; + } + /* clone the memory mapped registers for our use */ + regs_area = clone_area ( + "SAVAGE regs area", (void **)®s, B_ANY_ADDRESS, + B_READ_AREA | B_WRITE_AREA, si->regs_area); + if (regs_area < 0) + { + result = regs_area; + goto error1; + } + regs = __get_area_address (regs_area); + + dpf ("PCI VENDOR_ID = 0x%4X, DEVICE_ID = 0x%4X\n", + read16 ((byte_t*)regs + 0x8000), + read16 ((byte_t*)regs + 0x8002)); + + /* clone the framebuffer buffer for our use */ + fb_area = clone_area ( + "SAVAGE fb area", (void **)&framebuffer, B_ANY_ADDRESS, + B_READ_AREA | B_WRITE_AREA, si->fb_area); + if (fb_area < 0) + { + result = regs_area; + goto error2; + } + framebuffer = __get_area_address (fb_area); + + s3drv_init ((byte_t*)regs); + dpf ("s3drv_init called\n"); + s3accel_init (s3drv_get_context ()); + dpf ("s3accel_init called\n"); + s3drv_unlock_regs (); + + /* all done */ + goto error0; + +error2: + delete_area(regs_area); +error1: + delete_area(shared_info_area); +error0: + dpf ("init_common done\n"); + return result; +} + +/* Clean up code shared between primary and cloned accelrants */ +static void uninit_common(void) +{ + /* release framebuffer area */ + delete_area (fb_area); + /* 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 */ + { + SVGAMode orig, newmode; + PixelTiming timing = + { 0, { 640, 656, 752, 800, 0 }, { 480, 490, 492, 525, 0 } }; + s3drv_unlock_regs (); + s3drv_get_mode (&timing, 32, (byte_t*)&newmode, sizeof (newmode)); + s3drv_save_mode ((byte_t*)&orig, sizeof (orig)); + s3drv_restore_mode ((byte_t*)&newmode, sizeof (newmode), 1); + s3accel_init (s3drv_get_context ()); + //si->mem_size = s3drv_detect_vram_mb_size ((byte_t*)framebuffer, 32); + //dpf ("si->mem_size = %dMB\n", si->mem_size); + si->mem_size = s3drv_detect_vram_mb_size (NULL, 2); + dpf ("si->mem_size = %dMB\n", si->mem_size); + si->mem_size *= 1024*1024; + s3drv_restore_mode ((byte_t*)&orig, sizeof (orig), 1); + s3drv_lock_regs (); + } + + si->pix_clk_max8 = 250000; + si->pix_clk_max16 = 250000; + si->pix_clk_max32 = 250000; + + /* 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 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; + /* + 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); + si->start_addr = 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_SAVAGE_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) +{ + savage_device_name dn; + status_t result; + + /* call the kernel driver to get the device name */ + dn.magic = SAVAGE_PRIVATE_DATA_MAGIC; + /* store the returned info directly into the passed buffer */ + dn.name = (char *)data; + result = ioctl(fd, SAVAGE_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 = my_mode_list_area = clone_area( + "SAVAGE cloned display_modes", + (void **)&my_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(my_mode_list_area); + /* paranoia */ + my_mode_list = 0; + /* release our cloned data */ + uninit_common(); + /* close the file handle ONLY if we're the clone */ + if (accelerantIsClone) close(fd); +} + +status_t GET_ACCELERANT_DEVICE_INFO(accelerant_device_info *adi) +{ + status_t retval; + static const char * names1[] = + { "Savage3D", "Savage3D/MV", "Savage4", "Savage4", + "Savage4", "ProSavage PM133", "ProSavage KM133" }; + static const char * names2[] = + { "Savage/MX-MV", "Savage/MX", "Savage/IX-MV", "Savage/IX" }; + static const char * names3[] = + { "ProSavage PN133", "ProSavage KN133", "ProSavage P4M266", "ProSavage8 KM266" }; + + adi->version = 0x10000; + if (si->device_id == PCI_PID_SAVAGE2000) + { + strcpy (adi->name, "Savage2000"); + strcpy (adi->chipset, "Savage2000"); + } + else + { + const char ** names = NULL; + int offset = 0; + switch (si->device_id & 0xFFF0) + { + case 0x8a20: + names = names1; + offset = si->device_id - PCI_PID_SAVAGE3D; + break; + case 0x8c10: + names = names2; + offset = si->device_id - PCI_PID_SAVAGEMXMV; + break; + case 0x8d00: + names = names3; + offset = si->device_id - PCI_PID_PN133; + break; + } + if (names != NULL) + { + strcpy (adi->name, names[offset]); + strcpy (adi->chipset, names[offset]); + } + else + { + strcpy (adi->name, "not supported"); + strcpy (adi->chipset, "Savage"); + } + } + strcpy (adi->serial_no, "0"); + adi->memory = si->mem_size; + adi->dac_speed = 250; + + retval = B_OK; + return retval; +} diff --git a/src/add-ons/accelerants/s3savage/Jamfile b/src/add-ons/accelerants/s3savage/Jamfile new file mode 100644 index 0000000000..146fb77e40 --- /dev/null +++ b/src/add-ons/accelerants/s3savage/Jamfile @@ -0,0 +1,30 @@ +SubDir HAIKU_TOP src add-ons accelerants s3savage ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +UsePrivateHeaders graphics ; +UsePrivateHeaders [ FDirName graphics s3savage ] ; + +Addon s3savage.accelerant : accelerants : + Acceleration.c + Cursor.c + EngineManagement.c + GetAccelerantHook.c + GetModeInfo.c + GlobalData.c + InitAccelerant.c + ProposeDisplayMode.c + SetDisplayMode.c + + s3accel.c + s3drv.c + s3vga.c + + : false + : be +; + +Package haiku-s3savage-cvs : + s3savage.accelerant : + boot home config add-ons accelerants ; + diff --git a/src/add-ons/accelerants/s3savage/LICENSE b/src/add-ons/accelerants/s3savage/LICENSE new file mode 100644 index 0000000000..5311dbed10 --- /dev/null +++ b/src/add-ons/accelerants/s3savage/LICENSE @@ -0,0 +1,27 @@ +This is a modified version of the XFree86 license. + +The binary and source form of this software is covered by the +following copyright/license. + +Copyright (C) 1999 Erdi Chen. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + diff --git a/src/add-ons/accelerants/s3savage/ProposeDisplayMode.c b/src/add-ons/accelerants/s3savage/ProposeDisplayMode.c new file mode 100644 index 0000000000..25def93dc1 --- /dev/null +++ b/src/add-ons/accelerants/s3savage/ProposeDisplayMode.c @@ -0,0 +1,384 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +/* + * Copyright 1999 Erdi Chen + */ + +#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); + + status_t + result = B_OK; + 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; + + /* + 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; + break; + //case B_RGB15: + case B_RGB16: + limit_clock = si->pix_clk_max16; + row_bytes = 2; + break; + case B_RGB32: + limit_clock = si->pix_clk_max32; + row_bytes = 4; + 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) + ) result = B_BAD_VALUE; + + /* 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 */ + 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, my_mode_list, si->mode_count * 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[] = +// {B_CMAP8, B_RGB15_LITTLE, B_RGB16_LITTLE, B_RGB32_LITTLE}; + {B_CMAP8, B_RGB16_LITTLE, B_RGB32_LITTLE}; +#else + color_space spaces[] = +// {B_CMAP8, B_RGB15_BIG, B_RGB16_BIG, B_RGB32_BIG}; + {B_CMAP8, B_RGB16_BIG, B_RGB32_BIG}; +#endif + + /* figure out how big the list could be, and adjust up to nearest + multiple of B_PAGE_SIZE */ + max_size = (((MODE_COUNT * sizeof (spaces) / sizeof (color_space)) + * sizeof(display_mode)) + + (B_PAGE_SIZE-1)) & ~(B_PAGE_SIZE-1); + /* create an area to hold the info */ + si->mode_area = my_mode_list_area = + create_area ( + "SAVAGE accelerant mode info", (void **)&my_mode_list, + B_ANY_ADDRESS, max_size, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); + if (my_mode_list_area < B_OK) return my_mode_list_area; + + /* walk through our predefined list and see which modes fit this device */ + src = mode_list; + dst = my_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]; + if (spaces[j] != B_CMAP8) dst->flags &= ~B_8_BIT_DAC; + /* ask for a compatible mode */ + if (1 || 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; +} diff --git a/src/add-ons/accelerants/s3savage/SetDisplayMode.c b/src/add-ons/accelerants/s3savage/SetDisplayMode.c new file mode 100644 index 0000000000..9fd4703899 --- /dev/null +++ b/src/add-ons/accelerants/s3savage/SetDisplayMode.c @@ -0,0 +1,309 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +/* + * Copyright 1999 Erdi Chen + */ + +#include "GlobalData.h" +#include "generic.h" +#include "s3drv.h" +#include "s3accel.h" +#include "s3mmio.h" + +#include +#include + +/* + Enable/Disable interrupts. Just a wrapper around the + ioctl() to the kernel driver. + */ +static void interrupt_enable(bool flag) +{ + status_t result; + savage_set_bool_state sbs; + + /* set the magic number so the driver knows we're for real */ + sbs.magic = SAVAGE_PRIVATE_DATA_MAGIC; + sbs.do_it = flag; + /* contact driver and get a pointer to the registers and shared data */ + result = ioctl(fd, SAVAGE_RUN_INTERRUPTS, &sbs, sizeof(sbs)); +} + +/* + Calculates the number of bits for a given color_space. + Usefull for mode setup routines, etc. + */ +static 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. + 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) +{ + uint bpp; + SVGAMode s3mode; + PixelTiming timing; + status_t result; + + dpf ("do_set_display_mode begin\n"); + /* disable interrupts using the kernel driver */ + interrupt_enable(false); + + bpp = calcBitsPerPixel (dm->space); + + timing.dot_clock_khz = dm->timing.pixel_clock; + timing.x.disp = dm->timing.h_display; + timing.x.sync_start = dm->timing.h_sync_start; + timing.x.sync_end = dm->timing.h_sync_end; + timing.x.total = dm->timing.h_total; + timing.x.polarity = (dm->timing.flags & B_POSITIVE_HSYNC) ? 1 : 0; + timing.y.disp = dm->timing.v_display; + timing.y.sync_start = dm->timing.v_sync_start; + timing.y.sync_end = dm->timing.v_sync_end; + timing.y.total = dm->timing.v_total; + timing.y.polarity = (dm->timing.flags & B_POSITIVE_VSYNC) ? 1 : 0; + dpf ("TIMING = { %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d }\n", + timing.dot_clock_khz, + timing.x.disp, timing.x.sync_start, timing.x.sync_end, + timing.x.total, timing.x.polarity, + timing.y.disp, timing.y.sync_start, timing.y.sync_end, + timing.y.total, timing.y.polarity); + + s3drv_unlock_regs (); + memset (&s3mode, 0, sizeof (s3mode)); + result = s3drv_get_mode (&timing, bpp, (byte_t*)&s3mode, sizeof (s3mode)); + if (result > 0) dpf ("s3drv_get_mode succeeded\n"); + else dpf ("s3drv_get_mode failed\n"); + dpf ("depth = %d, width = %d, height = %d\n", + s3mode.pixel_size, s3mode.pixel_width, s3mode.pixel_height); + result = s3drv_restore_mode ((byte_t*)&s3mode, sizeof (s3mode), true); + if (result > 0) dpf ("s3drv_set_mode succeeded\n"); + else dpf ("s3drv_set_mode failed\n"); + + /* enable interrupts using the kernel driver */ + interrupt_enable(true); + + dpf ("si->start_addr = %d\n", si->start_addr); + s3drv_set_display_start (si->start_addr); + s3drv_set_logical_width (dm->virtual_width); + si->fbc.bytes_per_row = (dm->virtual_width * bpp + 7) / 8; + s3accel_init (s3drv_get_context ()); + dpf ("s3accel_init called\n"); + + //s3drv_lock_regs (); + dpf ("do_set_display_mode done\n"); +} + +/* + 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); + si->dm = target; + 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) +{ + /* + 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; + + /* actually set the registers */ + s3drv_adjust_viewport (h_display_start, v_display_start); + + 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. + */ + s3drv_unlock_regs (); + write_sr ((byte_t*)regs + 0x8000, 0x1B, 0x10); + while (count--) + { + s3drv_set_palette ( + first++, color_data[0], color_data[1], color_data[2]); + color_data += 3; + } + s3drv_lock_regs (); +} + + +/* 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. + */ + /* FIXME - read the bits */ + LTemp = *regs; + /* this mask is device dependant */ + LTemp &= ~BITSMASK; /* 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 |= H_SYNC_OFF | DISPLAY_OFF; + break; + case B_DPMS_SUSPEND: /* H: on, V: off, display off */ + LTemp |= V_SYNC_OFF | DISPLAY_OFF; + break; + case B_DPMS_OFF: /* H: off, V: off, display off */ + LTemp |= H_SYNC_OFF | V_SYNC_OFF | DISPLAY_OFF; + break; + default: + return B_ERROR; + } + /* FIXME - write the bits */ + *regs = LTemp; + + /* + 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 */ + /* FIXME - read the bits */ + LTemp = *regs; + + /* what mode is set? */ + switch (LTemp & (H_SYNC_OFF | V_SYNC_OFF)) + { + case 0: /* H: on, V: on */ + mode = B_DPMS_ON; + break; + case H_SYNC_OFF: /* H: off, V: on */ + mode = B_DPMS_STAND_BY; + break; + case V_SYNC_OFF: /* H: on, V: off */ + mode = B_DPMS_SUSPEND; + break; + case (H_SYNC_OFF | V_SYNC_OFF): /* H: off, V: off */ + mode = B_DPMS_OFF; + break; + } + return mode; +} diff --git a/src/add-ons/accelerants/s3savage/datatype.h b/src/add-ons/accelerants/s3savage/datatype.h new file mode 100644 index 0000000000..5f19d90304 --- /dev/null +++ b/src/add-ons/accelerants/s3savage/datatype.h @@ -0,0 +1,100 @@ +/* + * Copyright 1999 Erdi Chen + */ + +#ifndef __DATA_TYPES_H__ +#define __DATA_TYPES_H__ + + +#define TEXT_MODE_SIZE 256*1024 +#define TEXT_FONT_SIZE 32*1024 +#define DISPLAY_START 1024*0 + +#define CRTC_INDEX 0x3d4 +#define CRTC_DATA 0x3d5 +#define SEQ_INDEX 0x3c4 +#define SEQ_DATA 0x3c5 + +typedef unsigned char byte_t; +typedef unsigned short word_t; +typedef unsigned long dword_t; + +extern byte_t vga_mode_numbers[]; + +typedef struct +{ + byte_t col; + byte_t row; + byte_t height; + byte_t buf_size0; + byte_t buf_size1; + byte_t seq_regs[0x04]; + byte_t misc_output; + byte_t crtc_regs[0x19]; + byte_t attr_regs[0x14]; + byte_t graph_regs[0x09]; +} VGAMode; + +typedef struct +{ + byte_t col; + byte_t row; + byte_t height; + byte_t buf_size0; + byte_t buf_size1; + byte_t seq_regs[0x04]; + byte_t misc_output; + byte_t crtc_regs[0x19]; + byte_t attr_regs[0x14]; + byte_t graph_regs[0x09]; + byte_t refresh_rate; + word_t pixel_size; + word_t pixel_width; + word_t pixel_height; + byte_t sr12; + byte_t sr13; + byte_t sr29; + byte_t misc_ctrl; + byte_t cr43; + byte_t cr50; + byte_t cr51; + byte_t cr5d; + byte_t cr5e; + byte_t cr5f; + byte_t cr66; + byte_t cr67; + byte_t cr31; + byte_t cr58; + byte_t cr69; +} SVGAMode; + +typedef struct +{ + byte_t s3mode; + byte_t mode_tab0; + byte_t mode_tab1; + byte_t mode_attr; + byte_t cr66; + byte_t cr67; + byte_t seq4; + byte_t patch0; + byte_t patch1; +} SVGAModeInfo; + +typedef struct +{ + byte_t crtc0; + byte_t crtc1; + byte_t rate_attr; + byte_t rate_freq; + byte_t sr13; + byte_t sr12; + byte_t sr29; + byte_t misc_ctrl; + byte_t cr50; + byte_t cr5d; + byte_t cr5e; +} SVGAPatchTable; + + +#endif /* __DATA_TYPES_H__ */ diff --git a/src/add-ons/accelerants/s3savage/generic.h b/src/add-ons/accelerants/s3savage/generic.h new file mode 100644 index 0000000000..65b4231e86 --- /dev/null +++ b/src/add-ons/accelerants/s3savage/generic.h @@ -0,0 +1,148 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ + +#if !defined(GENERIC_H) +#define GENERIC_H + +#include + +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 + +#if 0 +typedef enum +{ + B_NO_COLOR_SPACE = 0x0000, /* byte in memory order, high bit first */ + + /* linear color space (little endian is the default) */ + B_RGB32 = 0x0008, /* B[7:0] G[7:0] R[7:0] -[7:0] */ + B_RGBA32 = 0x2008, /* B[7:0] G[7:0] R[7:0] A[7:0] */ + B_RGB24 = 0x0003, /* B[7:0] G[7:0] R[7:0] */ + B_RGB16 = 0x0005, /* G[2:0],B[4:0] R[4:0],G[5:3] */ + B_RGB15 = 0x0010, /* G[2:0],B[4:0] -[0],R[4:0],G[4:3] */ + B_RGBA15 = 0x2010, /* G[2:0],B[4:0] A[0],R[4:0],G[4:3] */ + B_CMAP8 = 0x0004, /* D[7:0] */ + B_GRAY8 = 0x0002, /* Y[7:0] */ + B_GRAY1 = 0x0001, /* Y0[0],Y1[0],Y2[0],Y3[0],Y4[0],Y5[0],Y6[0],Y7[0] */ + + /* big endian version, when the encoding is not endianess independant */ + B_RGB32_BIG = 0x1008, /* -[7:0] R[7:0] G[7:0] B[7:0] */ + B_RGBA32_BIG = 0x3008, /* A[7:0] R[7:0] G[7:0] B[7:0] */ + B_RGB24_BIG = 0x1003, /* R[7:0] G[7:0] B[7:0] */ + B_RGB16_BIG = 0x1005, /* R[4:0],G[5:3] G[2:0],B[4:0] */ + B_RGB15_BIG = 0x1010, /* -[0],R[4:0],G[4:3] G[2:0],B[4:0] */ + B_RGBA15_BIG = 0x3010, /* A[0],R[4:0],G[4:3] G[2:0],B[4:0] */ + + /* little-endian declarations, for completness */ + B_RGB32_LITTLE = B_RGB32, + B_RGBA32_LITTLE = B_RGBA32, + B_RGB24_LITTLE = B_RGB24, + B_RGB16_LITTLE = B_RGB16, + B_RGB15_LITTLE = B_RGB15, + B_RGBA15_LITTLE = B_RGBA15, + + /* non linear color space -- note that these are here for exchange purposes; */ + /* a BBitmap or BView may not necessarily support all these color spaces. */ + + /* Loss/Saturation points are Y 16-235 (absoulte); Cb/Cr 16-240 (center 128) */ + + B_YCbCr422 = 0x4000, /* Y0[7:0] Cb0[7:0] Y1[7:0] Cr0[7:0] Y2[7:0]... */ + /* Cb2[7:0] Y3[7:0] Cr2[7:0] */ + B_YCbCr411 = 0x4001, /* Cb0[7:0] Y0[7:0] Cr0[7:0] Y1[7:0] Cb4[7:0]...*/ + /* Y2[7:0] Cr4[7:0] Y3[7:0] Y4[7:0] Y5[7:0]... */ + /* Y6[7:0] Y7[7:0] */ + B_YCbCr444 = 0x4003, /* Y0[7:0] Cb0[7:0] Cr0[7:0] */ + B_YCbCr420 = 0x4004, /* Non-interlaced only, Cb0 Y0 Y1 Cb2 Y2 Y3 on even scan lines ... */ + /* Cr0 Y0 Y1 Cr2 Y2 Y3 on odd scan lines */ + + /* Extrema points are Y 0 - 207 (absolute) U -91 - 91 (offset 128) V -127 - 127 (offset 128) */ + /* note that YUV byte order is different from YCbCr */ + /* USE YCbCr, not YUV, when that's what you mean! */ + B_YUV422 = 0x4020, /* U0[7:0] Y0[7:0] V0[7:0] Y1[7:0] ... */ + /* U2[7:0] Y2[7:0] V2[7:0] Y3[7:0] */ + B_YUV411 = 0x4021, /* U0[7:0] Y0[7:0] Y1[7:0] V0[7:0] Y2[7:0] Y3[7:0] */ + /* U4[7:0] Y4[7:0] Y5[7:0] V4[7:0] Y6[7:0] Y7[7:0] */ + B_YUV444 = 0x4023, /* U0[7:0] Y0[7:0] V0[7:0] U1[7:0] Y1[7:0] V1[7:0] */ + B_YUV420 = 0x4024, /* Non-interlaced only, U0 Y0 Y1 U2 Y2 Y3 on even scan lines ... */ + /* V0 Y0 Y1 V2 Y2 Y3 on odd scan lines */ + B_YUV9 = 0x402C, /* planar? 410? */ + B_YUV12 = 0x402D, /* planar? 420? */ + + B_UVL24 = 0x4030, /* U0[7:0] V0[7:0] L0[7:0] ... */ + B_UVL32 = 0x4031, /* U0[7:0] V0[7:0] L0[7:0] X0[7:0]... */ + B_UVLA32 = 0x6031, /* U0[7:0] V0[7:0] L0[7:0] A0[7:0]... */ + + B_LAB24 = 0x4032, /* L0[7:0] a0[7:0] b0[7:0] ... (a is not alpha!) */ + B_LAB32 = 0x4033, /* L0[7:0] a0[7:0] b0[7:0] X0[7:0] ... (b is not alpha!) */ + B_LABA32 = 0x6033, /* L0[7:0] a0[7:0] b0[7:0] A0[7:0] ... (A is alpha) */ + + /* red is at hue = 0 */ + + B_HSI24 = 0x4040, /* H[7:0] S[7:0] I[7:0] */ + B_HSI32 = 0x4041, /* H[7:0] S[7:0] I[7:0] X[7:0] */ + B_HSIA32 = 0x6041, /* H[7:0] S[7:0] I[7:0] A[7:0] */ + + B_HSV24 = 0x4042, /* H[7:0] S[7:0] V[7:0] */ + B_HSV32 = 0x4043, /* H[7:0] S[7:0] V[7:0] X[7:0] */ + B_HSVA32 = 0x6043, /* H[7:0] S[7:0] V[7:0] A[7:0] */ + + B_HLS24 = 0x4044, /* H[7:0] L[7:0] S[7:0] */ + B_HLS32 = 0x4045, /* H[7:0] L[7:0] S[7:0] X[7:0] */ + B_HLSA32 = 0x6045, /* H[7:0] L[7:0] S[7:0] A[7:0] */ + + B_CMY24 = 0xC001, /* C[7:0] M[7:0] Y[7:0] No gray removal done */ + B_CMY32 = 0xC002, /* C[7:0] M[7:0] Y[7:0] X[7:0] No gray removal done */ + B_CMYA32 = 0xE002, /* C[7:0] M[7:0] Y[7:0] A[7:0] No gray removal done */ + B_CMYK32 = 0xC003, /* C[7:0] M[7:0] Y[7:0] K[7:0] */ + + /* compatibility declarations */ + B_MONOCHROME_1_BIT = B_GRAY1, + B_GRAYSCALE_8_BIT = B_GRAY8, + B_COLOR_8_BIT = B_CMAP8, + B_RGB_32_BIT = B_RGB32, + B_RGB_16_BIT = B_RGB15, + B_BIG_RGB_32_BIT = B_RGB32_BIG, + B_BIG_RGB_16_BIT = B_RGB15_BIG +} color_space; +#endif diff --git a/src/add-ons/accelerants/s3savage/s3accel.c b/src/add-ons/accelerants/s3savage/s3accel.c new file mode 100644 index 0000000000..bec24c537b --- /dev/null +++ b/src/add-ons/accelerants/s3savage/s3accel.c @@ -0,0 +1,337 @@ +/* + * Copyright 1999 Erdi Chen + */ + + +#include "datatype.h" +#include "s3mmio.h" +#include "s3accel.h" +#include "s3drv.h" + + +#define MIX_MASK 0x000f + +#define MIX_NOT_DST 0x0000 +#define MIX_0 0x0001 +#define MIX_1 0x0002 +#define MIX_DST 0x0003 +#define MIX_NOT_SRC 0x0004 +#define MIX_XOR 0x0005 +#define MIX_XNOR 0x0006 +#define MIX_SRC 0x0007 +#define MIX_NAND 0x0008 +#define MIX_NOT_SRC_OR_DST 0x0009 +#define MIX_SRC_OR_NOT_DST 0x000a +#define MIX_OR 0x000b +#define MIX_AND 0x000c +#define MIX_SRC_AND_NOT_DST 0x000d +#define MIX_NOT_SRC_AND_DST 0x000e +#define MIX_NOR 0x000f + + +byte_t s3alu[16] = +{ + MIX_0, + MIX_AND, + MIX_SRC_AND_NOT_DST, + MIX_SRC, + MIX_NOT_SRC_AND_DST, + MIX_DST, + MIX_XOR, + MIX_OR, + MIX_NOR, + MIX_XNOR, + MIX_NOT_DST, + MIX_SRC_OR_NOT_DST, + MIX_NOT_SRC, + MIX_NOT_SRC_OR_DST, + MIX_NAND, + MIX_1 +}; + + +byte_t * __vga_base = 0; +byte_t * __mmio_base = 0; +dword_t s3accelCmd = 0; + + +typedef struct +{ + S3DriverContext * s3dc; + byte_t * mmio_base; + byte_t * vga_base; + byte_t * bci_base; + byte_t * fb_base; + int fb_size; + word_t device_id; + dword_t global_bd; + dword_t primary_bd; + dword_t secondary_bd; + int bpp; + int logical_width; + int logical_height; +} S3AccelContext; + +static S3AccelContext S3AC = { 0 }; + + +int s3accel_init (S3DriverContext * s3dc) +{ + byte_t sr1; + int stride; + int display_start; + + if (!s3dc) return 0; + S3AC.s3dc = s3dc; + S3AC.mmio_base = __mmio_base = S3AC.s3dc->mmio_base; + S3AC.vga_base = __vga_base = S3AC.s3dc->vga_base; + S3AC.fb_base = S3AC.s3dc->fb_base; + S3AC.fb_size = S3AC.s3dc->fb_size; + S3AC.bpp = S3AC.s3dc->bpp; + S3AC.logical_width = S3AC.s3dc->logical_width; + + s3drv_unlock_regs (); + write32 (S3AC.mmio_base + 0x850C, 0x11); + write32 (S3AC.mmio_base + 0x850C, 0x11); + S3AC.device_id = read16 (__vga_base + 2); + sr1 = read_sr (__vga_base, 1); + write_sr (__vga_base, 1, sr1 | 0x20); + + UpdateGEReg (ALT_CURXY, 0); + UpdateGEReg (ALT_STEP, 0); + UpdateGEReg (ERR_TERM, 0); + UpdateGEReg (SHORT_STROKE,0); + UpdateGEReg (BKGD_COLOR, 0x00000000); + UpdateGEReg (FRGD_COLOR, 0xffffffff); + UpdateGEReg (WRT_MASK, 0xffffffff); + UpdateGEReg (RD_MASK, 0xffffffff); + UpdateGEReg (COLOR_CMP, 0x00000000); + UpdateGEReg (ALT_MIX, 0x00270007); + UpdateGEReg (SCISSORS_LT, 0x20001000); + UpdateGEReg (SCISSORS_RB, 0x4fff3fff); + UpdateGEReg (PIX_CNTL, 0xd000a000); + UpdateGEReg (MULT_MISC, 0xf000e200); + UpdateGEReg (ALT_PCNT, 0); + + display_start = S3AC.s3dc->display_start; + s3drv_dpf ("display_start = %d\n", display_start); + write32 (__mmio_base + 0x81c0, display_start); + stride = (S3AC.logical_width * S3AC.bpp + 7) >> 3; + write32 (__mmio_base + 0x81c8, stride); /* set stride */ + write32 (__mmio_base + 0x8168, display_start); + S3AC.global_bd = (S3AC.logical_width & 0x1ff0)|(S3AC.bpp<<16)|0x10000001; + /* Need to write GBD twice after an engine reset */ + write32 (__mmio_base + 0x816c, S3AC.global_bd); + write32 (__mmio_base + 0x816c, S3AC.global_bd); + S3AC.s3dc->global_bd = S3AC.global_bd; + write_sr (__vga_base, 1, sr1); + s3drv_lock_regs (); + return 1; +} + +int s3accel_wait_for_idle () +{ + s3drv_wait_for_idle (); + UpdateGEReg (WRT_MASK, 0xffffffff); + UpdateGEReg (RD_MASK, 0xffffffff); + UpdateGEReg (ALT_MIX, 0x00270007); + UpdateGEReg (SCISSORS_LT, 0x20001000); + UpdateGEReg (SCISSORS_RB, 0x4fff3fff); + UpdateGEReg (PIX_CNTL, 0xd000a000); + + return 1; +} + +int s3accel_wait_for_fifo (int count) +{ + int i; + s3drv_wait_for_idle (); + i = 0; + return i; +} + +__inline__ void s3accel_set_fg_color (dword_t color) +{ + s3accel_wait_for_fifo (1); + UpdateGEReg (FRGD_COLOR, color); +} + +__inline__ void s3accel_set_bg_color (dword_t color) +{ + s3accel_wait_for_fifo (1); + UpdateGEReg (BKGD_COLOR, color); +} + +void s3accel_set_clip_rect (int x1, int y1, int x2, int y2) +{ + s3accel_wait_for_fifo (2); + UpdateGEReg (SCISSORS_LT, (y1 << 16) | (x1 & 0xffff)); + UpdateGEReg (SCISSORS_RB, (y2 << 16) | (x2 & 0xffff)); +} + +#if S3ACCEL_NEED_FUNCTIONS + +void s3accel_setup_fill_rect (int color, int rop, dword_t planemask) +{ + s3accel_wait_for_fifo (2); + UpdateGEReg (WRT_MASK, planemask); + UpdateGEReg16 (FRGD_MIX, 0x0020 | s3alu[rop]); + s3drv_dpf ("FRGD_MIX is 0x%4X\n", 0x0020 | s3alu[rop]); + UpdateGEReg (FRGD_COLOR, color); + s3accelCmd = 0x40b1; +} + +void s3accel_repeat_fill_rect (int x, int y, int w, int h) +{ + w--; h--; + s3accel_wait_for_fifo (2); + UpdateGEReg (ALT_CURXY, (x<<16)|y); + UpdateGEReg (ALT_PCNT, (w<<16)|h); + UpdateGEReg (CMD, 0x000040b1); +} + +void s3accel_fill_rect (int x, int y, int w, int h) +{ + w--; h--; + UpdateGEReg16 (FRGD_MIX, 0x0027); + write32 (__mmio_base + ALT_CURXY, (x<<16)|y); + UpdateGEReg (ALT_PCNT, (w<<16)|h); + UpdateGEReg (CMD, 0x000040b1); +} + +__inline__ void s3accel_setup_copy_rect ( + int xdir, int ydir, int rop, dword_t planemask, + int transparency_color) +{ + s3accelCmd = 0xc011; + if (xdir == 1) s3accelCmd |= 0x20; + if (ydir == 1) s3accelCmd |= 0x80; + s3accel_wait_for_fifo (2); + UpdateGEReg (WRT_MASK, planemask); + if (transparency_color != -1) + { + UpdateGEReg (COLOR_CMP, transparency_color); + UpdateGEReg16 (PIX_CNTL, 0xa100); + } + UpdateGEReg16 (FRGD_MIX, 0x0060 | s3alu[rop]); +} + +__inline__ void s3accel_repeat_copy_rect ( + int src_x, int src_y, int dest_x, int dest_y, int w, int h) +{ + w--; h--; + if (!(s3accelCmd & 0x20)) + { + src_x += w; dest_x += w; + } + if (!(s3accelCmd & 0x80)) + { + src_y += h; dest_y += h; + } + + s3accel_wait_for_fifo (4); + UpdateGEReg (ALT_CURXY, (src_x<<16)|src_y); + UpdateGEReg (ALT_STEP, (dest_x<<16)|dest_y); + UpdateGEReg (ALT_PCNT, (w<<16)|h); + UpdateGEReg (CMD, s3accelCmd); +} + +void s3accel_copy_rect (int is_patterned, int src_x, int src_y, + int dest_x, int dest_y, int w, int h) +{ + int cmd = 0xc0b1; + + w--; h--; + if (is_patterned) + cmd |= 0x4000; + else + { + if ((src_x < dest_x) )//&& (src_x + w) > dest_x) + { + src_x += w; dest_x += w; + cmd ^= 32; + } + if ((src_y < dest_y) )//&& (src_y + h) > dest_y) + { + src_y += h; dest_y += h; + cmd ^= 128; + } + } + + UpdateGEReg16 (FRGD_MIX, 0x0067); + UpdateGEReg (ALT_CURXY, (src_x<<16)|src_y); + UpdateGEReg (ALT_STEP, (dest_x<<16)|dest_y); + UpdateGEReg (ALT_PCNT, (w<<16)|h); + UpdateGEReg (CMD, cmd); +} + +void s3accel_draw_line (int x1, int y1, int x2, int y2, int no_endpoint) +{ + int cmd = 0x20b1; + int min, max, err_term, *tmp=&max; + + /* + max = max(abs(x2-x1),abs(y2-y1)); + min = min(abs(x2-x1),abs(y2-y1)); + */ + + max = x2 - x1; + min = y2 - y1; + + if (max < 0) + { + max = -max; + cmd ^= 32; + } + + if (min < 0) + { + min = -min; + cmd ^= 128; + } + + if (max < min) + { + max = min; + min = &tmp; + cmd |= 0x40; + } + + err_term = 2 * min - max; + if (!(cmd & 0x20)) err_term --; + if (no_endpoint) cmd |= 4; + + s3accel_wait_for_idle (); + UpdateGEReg16 (FRGD_MIX, 0x0027); + UpdateGEReg (ALT_CURXY, (x1<<16)|y1); + /* Databook says MAJ_AXIS_PCNT = max - 1, + but MAJ_AXIS_PCNT = max seems to produce correct result. */ + UpdateGEReg16 (MAJ_AXIS_PCNT, max); + UpdateGEReg (ALT_STEP, ((2*(min-max))<<16)|(2*min)); + UpdateGEReg (ERR_TERM, err_term); + UpdateGEReg (CMD, cmd); +} + + +void s3accel_repeat_draw_line ( + int x1, int y1, int x2, int y2, dword_t bias) +{ + s3accel_draw_line (x1, y1, x2, y2, bias & 0x100); +} + + +void s3accel_write_image (int x, int y, int w, int h) +{ + int count; + + count = (((w * S3AC.bpp) + 31) >> 5) * h; + w--; h--; + s3accel_wait_for_idle (); + UpdateGEReg16 (FRGD_MIX, 0x0047); + UpdateGEReg (ALT_CURXY, (x<<16)|y); /* destination */ + UpdateGEReg (ALT_PCNT, (w<<16)|h); /* width/height */ + UpdateGEReg (CMD, 0x55b1); + + while (count --) { write32 (__mmio_base, ~0); } +} +#endif /* S3ACCEL_NEED_FUNCTIONS */ diff --git a/src/add-ons/accelerants/s3savage/s3accel.h b/src/add-ons/accelerants/s3savage/s3accel.h new file mode 100644 index 0000000000..99dff9fe88 --- /dev/null +++ b/src/add-ons/accelerants/s3savage/s3accel.h @@ -0,0 +1,112 @@ +/* + * Copyright 1999 Erdi Chen + */ + + +#ifndef __S3ACCEL_H__ +#define __S3ACCEL_H__ + + +#include "s3drv.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum +{ + ALT_CURXY = 0x8100, + CUR_Y = 0x8100, + CUR_X = 0x8102, + ALT_STEP = 0x8108, + DESTY_AXSTP = 0x8108, + DESTX_DIASTP = 0x810a, + ERR_TERM = 0x8110, + CMD = 0x8118, + SHORT_STROKE = 0x811c, + BKGD_COLOR = 0x8120, + FRGD_COLOR = 0x8124, + WRT_MASK = 0x8128, + RD_MASK = 0x812c, + COLOR_CMP = 0x8130, + ALT_MIX = 0x8134, + BKGD_MIX = 0x8134, + FRGD_MIX = 0x8136, + SCISSORS_LT = 0x8138, + SCISSORS_T = 0x8138, + SCISSORS_L = 0x813a, + SCISSORS_RB = 0x813c, + SCISSORS_B = 0x813c, + SCISSORS_R = 0x813e, + PIX_CNTL = 0x8140, + MULT_MISC2 = 0x8142, + MULT_MISC = 0x8144, + READ_SEL = 0x8144, + ALT_PCNT = 0x8148, + MIN_AXIS_PCNT = 0x8148, + MAJ_AXIS_PCNT = 0x814a +} S3GERegister; + +extern byte_t s3alu[16]; +extern byte_t * __vga_base; +extern byte_t * __mmio_base; + + +#define UpdateGEReg(reg, val)\ + write32 (__mmio_base + reg, val);\ + +#define UpdateGEReg16(reg, val)\ + write16 (__mmio_base + reg, val);\ + + +/* + * Function prototypes. + */ +extern int s3accel_init (S3DriverContext * s3dc); +extern int s3accel_wait_for_idle (); +extern int s3accel_wait_for_fifo (int count); +extern void s3accel_set_pattern (); +extern void s3accel_set_fg_color (dword_t fg_color); +extern void s3accel_set_bg_color (dword_t bg_color); +extern void s3accel_set_clip_rect (int x1, int y1, int x2, int y2); + +extern void s3accel_setup_copy_rect ( + int xdir, int ydir, int rop, + dword_t planemask, int transparency_color); +extern void s3accel_repeat_copy_rect ( + int x1, int y1, int x2, int y2, int w, int h); +extern void s3accel_copy_rect (int is_patterned, int src_x, int src_y, + int dest_x, int dest_y, int w, int h); + +extern void s3accel_setup_fill_rect (int color, int rop, dword_t planemask); +extern void s3accel_repeat_fill_rect (int x, int y, int w, int h); +extern void s3accel_fill_rect (int x, int y, int w, int h); + +extern void s3accel_fill_polygon (); +extern void s3accel_setup_fill_polygon (); +extern void s3accel_repeat_fill_polygon (int count, short * xw); + +extern void s3accel_draw_line (int x1, int y1, int x2, int y2, + int no_endpoint); +extern void s3accel_draw_polyline (int count, short * xy); +extern void s3accel_draw_segments (int count, short * xy); +extern void s3accel_setup_draw_line (); +extern void s3accel_repeat_draw_line (int x1, int y1, int x2, int y2, + dword_t bias); + +extern void s3accel_draw_scanline (); +extern void s3accel_setup_draw_scanline (); +extern void s3accel_repeat_draw_scanline (int x, int width); +extern void s3accel_draw_multi_scanlines (int count, int starty, short * xw); + +extern void s3accel_write_image (int x, int y, int w, int h); +extern void s3accel_write_bitmap (); + + +#ifdef __cplusplus +} +#endif + + +#endif /* __S3ACCEL_H__ */ diff --git a/src/add-ons/accelerants/s3savage/s3drv.c b/src/add-ons/accelerants/s3savage/s3drv.c new file mode 100644 index 0000000000..2f2e950376 --- /dev/null +++ b/src/add-ons/accelerants/s3savage/s3drv.c @@ -0,0 +1,683 @@ +/* + * Copyright 1999 Erdi Chen + */ + + +#include "s3mmio.h" +#include "s3drv.h" +#include "DriverInterface.h" + +#include +#include + + +extern int __s3drv_get_svga_mode ( + SVGAMode * svgamode, PixelTiming * pixel_timing); + +static S3DriverContext S3DC = +{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +void s3drv_dpf (const char * format, ...) +{ +#if DEBUG > 0 + va_list args; + char buffer[4096]; + + va_start (args, format); + vsprintf (buffer, format, args); + fputs (buffer, stderr); + va_end (args); +#endif +} + +S3DriverContext * s3drv_get_context () +{ + return &S3DC; +} + +int s3drv_init (byte_t * mb) +{ + if (!mb) return 0; + S3DC.mmio_base = mb; + S3DC.vga_base = mb + 0x8000; + S3DC.device_id = read16 (S3DC.vga_base + 2); + S3DC.viewport_x = 0; + S3DC.viewport_y = 0; + S3DC.display_start = DISPLAY_START; + write32 (mb + 0x8510, 1); + return 1; +} + + +static int __reg_unlock_count = 0; + +void s3drv_lock_regs () +{ + __reg_unlock_count --; + if (__reg_unlock_count < 1) + { + __reg_unlock_count = 0; + s3_lock_regs (S3DC.vga_base); + } +} + +void s3drv_unlock_regs () +{ + s3_unlock_regs (S3DC.vga_base); + __reg_unlock_count ++; +} + +int s3drv_detect_vram_mb_size (byte_t * vram_start, int max_mb) +{ + int ram_size_mb; + byte_t * vga_base = S3DC.vga_base; + + s3drv_unlock_regs (); + + if (vram_start) + { + byte_t cr58, cr66; + byte_t * magic_spot; + + if (!vga_base) return 2; + if (!vram_start) return 2; + + magic_spot = vram_start + 1024*1024; + cr58 = read_cr (vga_base, 0x58); + write_cr (vga_base, 0x58, 0x13); + cr66 = read_cr (vga_base, 0x66); + write_cr (vga_base, 0x66, cr66 | 1); + for (ram_size_mb = 1; ram_size_mb < max_mb; ram_size_mb ++) + { + read32 (vga_base); + write32 (magic_spot, 0xC55CC55C); + if (read32 (magic_spot) != 0xC55CC55C) break; + write32 (vram_start, 0); + write32 (magic_spot, 0xFFFFFFFF); + if (read32 (vram_start) == 0xFFFFFFFF) break; + magic_spot += 1024*1024; + } + write_cr (vga_base, 0x58, cr58); + write_cr (vga_base, 0x66, cr66); + } + else + { + byte_t cr36; + word_t device; + + device = (read_cr (vga_base, 0x2d) << 8) | read_cr (vga_base, 0x2e); + cr36 = read_cr (vga_base, 0x36); + ram_size_mb = 2; + if (isSavage4Family(device)) + { + switch (cr36 >> 5) + { + case 0: + ram_size_mb = 2; + break; + case 1: + ram_size_mb = 4; + case 2: + ram_size_mb = 8; + break; + case 3: + ram_size_mb = 12; + break; + case 4: + ram_size_mb = 16; + break; + case 5: + ram_size_mb = 32; + break; + case 6: + ram_size_mb = 64; + break; + case 7: + ram_size_mb = 128; + break; + case 8: + ram_size_mb = 256; + break; + } + } + else + { + switch (cr36 >> 6) + { + case 0: + ram_size_mb = 8; + break; + case 1: + case 2: + ram_size_mb = 4; + break; + case 3: + ram_size_mb = 2; + break; + } + } + } + + s3drv_lock_regs (); + + return ram_size_mb; +} + +int s3drv_is_text_mode () +{ + return !(read_gr (S3DC.vga_base, 0x06) & 1); +} + +int s3drv_save_font (byte_t * vram, byte_t * font_buf, int buf_size) +{ + int i; + byte_t * font; + byte_t * plane2; + if (!font_buf) return 0; + font = font_buf; + plane2 = vram + 2; + *font = *plane2; + for (i = 0; i < buf_size; i ++) *(font++) = *(plane2+=4); + return 1; +} + +int s3drv_restore_font (byte_t * vram, byte_t * font_buf, int buf_size) +{ + int i; + byte_t * font; + byte_t * plane2; + if (!font_buf) return 0; + plane2 = vram + 2; + font = font_buf; + *plane2 = *font; + for (i = 0; i < buf_size; i ++) *(plane2+=4) = *(font++); + return 1; +} + +int s3drv_get_mode (PixelTiming * timing, int bpp, + byte_t * mode_buf, int buf_size) +{ + SVGAMode * s3mode; + if (!timing || !mode_buf) return 0; + if (buf_size < sizeof (SVGAMode)) return - sizeof (SVGAMode); + + s3mode = (SVGAMode*) mode_buf; + + __s3drv_get_svga_mode(s3mode, timing); + s3mode->pixel_size = bpp; + s3mode->pixel_width = timing->x.disp; + s3mode->pixel_height = timing->y.disp; + s3mode->seq_regs[0] = 0x01; + s3mode->seq_regs[1] = 0x0F; + s3mode->seq_regs[2] = 0x00; + s3mode->seq_regs[3] = 0x0E; + s3mode->attr_regs[0x10] = 0x01; + s3mode->attr_regs[0x11] = 0x00; + s3mode->attr_regs[0x12] = 0x0F; + s3mode->attr_regs[0x13] = 0x00; + s3mode->graph_regs[0] = 0x00; + s3mode->graph_regs[1] = 0x00; + s3mode->graph_regs[2] = 0x00; + s3mode->graph_regs[3] = 0x00; + s3mode->graph_regs[4] = 0x00; + s3mode->graph_regs[5] = 0x00; + s3mode->graph_regs[6] = 0x05; + s3mode->graph_regs[7] = 0x0F; + s3mode->graph_regs[8] = 0xFF; + s3mode->misc_ctrl = s3mode->misc_output & 0xc0; + s3mode->cr31 = 0x0d; + /* Savage3D and Savage/MX need double horizontal timing for 16bpp */ + s3mode->cr43 = (!isSavage4Family(S3DC.device_id) && + (((bpp + 7)/8) == 2) ) ? 0x80 : 0; + s3mode->cr50 = 0xC1 | ((((bpp+7)>>3) - 1) << 4); + s3mode->cr51 = + (((s3mode->pixel_width * s3mode->pixel_size + 31) >> 6) & 0x300) >> 4; + s3mode->cr58 = 0x13; + s3mode->cr66 = 0x89; + + switch (bpp) + { + case 8: + s3mode->cr67 = 0x00; + break; + case 15: + s3mode->cr67 = 0x20; + break; + case 16: + s3mode->cr67 = 0x40; + break; + case 32: + s3mode->cr67 = 0xd0; + break; + default: + s3mode->cr67 = 0x00; + } + s3mode->cr69 = 0x80; + return 1; +} + +int s3drv_display_is_off () +{ + return read_sr (S3DC.vga_base, 0x01) & 0x20; +} + +void s3drv_turn_display_off () +{ + write_sr (S3DC.vga_base, 0x01, read_sr (S3DC.vga_base, 0x01) | 0x20); +} + +void s3drv_turn_display_on () +{ + byte_t sr1 = read_sr (S3DC.vga_base, 0x01) & ~0x20; + s3drv_wait_for_vsync (); + write_sr (S3DC.vga_base, 0x01, sr1); +} + +int s3drv_set_logical_width (int width) +{ + if (width >= S3DC.width) + { + int stride; + width += 15; + width &= ~15; + stride = (width * S3DC.bpp + 7) >> 3; + S3DC.logical_width = width; + write32 (S3DC.mmio_base + 0x81c8, stride); /* set stride */ + if (S3DC.global_bd) + { + S3DC.global_bd = (S3DC.global_bd & 0xffffe00f) | stride; + write32 (S3DC.mmio_base + 0x816c, S3DC.global_bd); + write32 (S3DC.mmio_base + 0x816c, S3DC.global_bd); + } + } + + return S3DC.logical_width; +} + +int s3drv_adjust_viewport (int x, int y) +{ + int viewport_start; + + if (x < 0 || y < 0) return 0; + S3DC.viewport_x = x; + S3DC.viewport_y = y; + viewport_start = ((y * S3DC.logical_width + x) * S3DC.bpp + 7) / 8; + viewport_start += S3DC.display_start; + if (S3DC.bpp == 32) + { + S3DC.cursor_adjustment = (viewport_start & 15) / 4; + viewport_start &= ~15; + } + write32 (S3DC.mmio_base + 0x81c0, viewport_start); + return 1; +} + +int s3drv_set_display_start (int offset) +{ + if (offset > -1) + { + S3DC.display_start = offset + DISPLAY_START; + write32 (S3DC.mmio_base + 0x8168, S3DC.display_start); + s3drv_adjust_viewport (S3DC.viewport_x, S3DC.viewport_y); + } + return offset; +} + +void s3drv_wait_for_vsync () +{ + int i; + if (!s3drv_display_is_off ()) + for (i = 0; i < 1000; i ++) + if ( (read8 (S3DC.vga_base + 0x3da) & 9) == 9) break; +} + +void s3drv_wait_for_idle () +{ + int i; + if (isSavage4Family(S3DC.device_id)) for (i = 0; i < 10000000; i ++) + { + if ( (read32 (S3DC.mmio_base + 0x48c00) & 0x5e01ffff) == 0x5e000000) + break; + } + else for (i = 0; i < 10000000; i ++) + { + if ( (read32 (S3DC.mmio_base + 0x48c00) & 0x005fffff) == 0x005e0000) + break; + } +} + +int s3drv_set_cursor_color (dword_t fg_color, dword_t bg_color) +{ + read_cr (S3DC.vga_base, 0x45); + if (S3DC.bpp != 8) + { + write_cr (S3DC.vga_base, 0x4a, fg_color >> 16); + write_cr (S3DC.vga_base, 0x4a, fg_color >> 8); + } + write_cr (S3DC.vga_base, 0x4a, fg_color); + + read_cr (S3DC.vga_base, 0x45); + if (S3DC.bpp != 8) + { + write_cr (S3DC.vga_base, 0x4b, bg_color >> 16); + write_cr (S3DC.vga_base, 0x4b, bg_color >> 8); + } + write_cr (S3DC.vga_base, 0x4b, bg_color); + + return 1; +} + +/* Assume width and height are byte-aligned. */ + +int s3drv_load_cursor ( + byte_t * lfb, int offset, int width, int height, + byte_t * and_mask, byte_t * xor_mask) +{ + int i, x, y, bit_shift; + byte_t and_buf[512]; + byte_t xor_buf[512]; + byte_t * and_ptr = (byte_t*)and_mask; + byte_t * xor_ptr = (byte_t*)xor_mask; + + if (!lfb || !and_mask || !xor_mask) return 0; + + bit_shift = width & 7; + width /= 8; + + if (width != 64 || height != 64) + { + and_ptr = (byte_t*)and_buf; + xor_ptr = (byte_t*)xor_buf; + + for (y = 0; y < height; y ++) + { + for (x = 0; x < width; x ++) + { + *and_ptr ++ = *and_mask ++; + *xor_ptr ++ = *xor_mask ++; + } + + if (bit_shift) + { + x ++; + *and_ptr ++ = 0xFF ^ ((*and_mask ++) & (~(0xFF >> bit_shift))); + *xor_ptr ++ = (*xor_mask ++) & (~(0xFF >> bit_shift)); + } + + for ( ; x < 8; x ++) + { + *and_ptr ++ = 0xFF; + *xor_ptr ++ = 0x00; + } + } + + for ( ; y < 64; y ++) + { + *(dword_t*)and_ptr = ~0; and_ptr += 4; + *(dword_t*)and_ptr = ~0; and_ptr += 4; + *(dword_t*)xor_ptr = 0; xor_ptr += 4; + *(dword_t*)xor_ptr = 0; xor_ptr += 4; + } + + and_ptr = (byte_t*)and_buf; + xor_ptr = (byte_t*)xor_buf; + } + + lfb += offset * 1024; + write_cr (S3DC.vga_base, 0x4d, offset); + write_cr (S3DC.vga_base, 0x4c, offset >> 8); + + for (i = 0; i < 256; i ++) + { +#if 0 + dword_t tmp = (*(word_t*)and_ptr << 16) | *(word_t*)xor_ptr; + *(dword_t*)lfb = tmp; lfb += 4; +#else + write16 (lfb, *(word_t*)and_ptr); + lfb += 2; + write16 (lfb, *(word_t*)xor_ptr); + lfb += 2; +#endif + and_ptr += 2; xor_ptr += 2; + } + + return 1; +} + +void s3drv_move_cursor (int x, int y) +{ + int xoff = 0, yoff = 0; + + x += S3DC.cursor_adjustment; + if (x < 0) + { + xoff = (x < -63) ? 63 : -x; + x = 0; + } + if (y < 0) + { + yoff = (y < -63) ? 63 : -y; + y = 0; + } + + write_cr (S3DC.vga_base, 0x46, (x & 0xff00) >> 8); + write_cr (S3DC.vga_base, 0x47, (x & 0xff)); + write_cr (S3DC.vga_base, 0x49, (y & 0xff)); + write_cr (S3DC.vga_base, 0x4e, (xoff)); + write_cr (S3DC.vga_base, 0x4f, (yoff)); + write_cr (S3DC.vga_base, 0x48, (y & 0xff00) >> 8); +} + +void s3drv_show_cursor () +{ + write_cr (S3DC.vga_base, 0x45, read_cr (S3DC.vga_base, 0x45) | 1); +} + +void s3drv_hide_cursor () +{ + write_cr (S3DC.vga_base, 0x45, read_cr (S3DC.vga_base, 0x45) & ~1); +} + +void s3drv_set_palette (int index, int red, int green, int blue) +{ + byte_t * vga_base = S3DC.vga_base; + write8 (vga_base + 0x3c8, index); + write8 (vga_base + 0x3c9, red); + write8 (vga_base + 0x3c9, green); + write8 (vga_base + 0x3c9, blue); +} + +void s3drv_get_palette (int index, int *red, int *green, int *blue) +{ + byte_t * vga_base = S3DC.vga_base; + write8 (vga_base + 0x3c7, index); + *red = read8 (vga_base + 0x3c9); + *green = read8 (vga_base + 0x3c9); + *blue = read8 (vga_base + 0x3c9); +} + +int s3drv_save_mode (byte_t * mode_buf, int buf_size) +{ + int i; + byte_t * vga_base = S3DC.vga_base; + SVGAMode * mode = (SVGAMode*)(mode_buf); + + if (!mode_buf || buf_size < sizeof (SVGAMode)) + { + return - sizeof (SVGAMode); + } + + s3drv_unlock_regs (); + for (i = 0; i < 4; i ++) mode->seq_regs[i] = read_sr (vga_base, i+1); + mode->misc_output = read8 (vga_base + 0x3cc); + for (i = 0; i < 0x19; i ++) mode->crtc_regs[i] = read_cr (vga_base, i); + for (i = 0; i < 0x14; i ++) mode->attr_regs[i] = read_ar (vga_base, i); + for (i = 0; i < 0x09; i ++) mode->graph_regs[i] = read_gr (vga_base, i); + mode->refresh_rate = 0xff; + mode->sr12 = read_sr (vga_base, 0x12); + mode->sr13 = read_sr (vga_base, 0x13); + mode->sr29 = read_sr (vga_base, 0x29); + mode->misc_ctrl = mode->misc_output & 0xc0; + mode->cr43 = read_cr (vga_base, 0x43); + mode->cr50 = read_cr (vga_base, 0x50); + mode->cr51 = read_cr (vga_base, 0x51); + mode->cr5d = read_cr (vga_base, 0x5d); + mode->cr5e = read_cr (vga_base, 0x5e); + mode->cr5f = read_cr (vga_base, 0x5f); + mode->cr66 = read_cr (vga_base, 0x66); + mode->cr67 = read_cr (vga_base, 0x67); + mode->cr31 = read_cr (vga_base, 0x31); + mode->cr58 = read_cr (vga_base, 0x58); + mode->cr69 = read_cr (vga_base, 0x69); + mode->pixel_width = + ( ( ((mode->cr5f & 0x0c) << 7) | + ((mode->cr5d & 0x02) << 7) | + ( mode->crtc_regs[1] ) ) + 1 ) * 8; + mode->pixel_height = 1 + + ( ((mode->cr5e & 0x02) << 9) | + ((mode->crtc_regs[0x07] & 0x40) << 3) | + ((mode->crtc_regs[0x07] & 0x02) << 7) | + ( mode->crtc_regs[0x12] ) ); + + switch ( (mode->cr67 & 0xf0) >> 4) + { + case 0: + case 1: + mode->pixel_size = (read_cr (vga_base, 0x3a) & 0x10) ? 8 : 4; + break; + case 2: + case 3: + mode->pixel_size = 15; + break; + case 4: + case 5: + mode->pixel_size = 16; + break; + case 0xd: + mode->pixel_size = 32; + break; + default: + mode->pixel_size = 8; + } + s3drv_lock_regs (); + + return sizeof (SVGAMode); +} + +static word_t crtc_reg_defaults[] = +{ + 0x4838, 0xa539, 0x0531, 0x4032, 0x0833, 0x0034, 0x0035, 0x053a, + 0x103c, 0x0040, 0x0042, 0x0043, 0x0045, 0x0050, 0x0051, 0x0053, + 0x0055, 0x0058, 0x005d, 0x005e, 0x005f, 0x005b, 0x0066, 0x0067, + 0x0069, 0x006a, 0xc071, 0x0090, + 0x0087, 0x3070, 0xc071, 0x0772, 0x1f73, 0x1f74, 0x1f75, 0x0f76, + 0x1f77, 0x0178, 0x0179, 0x1f7a, 0x1f7b, 0x171c, 0x177d, 0x177e, + 0x0060, + 0x0000 +}; + +static word_t seq_reg_defaults[] = +{ + 0x0608, 0x0014, 0x4018, 0x0029, 0x601c, + 0x0000 +}; + +int s3drv_restore_mode (byte_t * mode_buf, int buf_size, int restore_vga) +{ + int i; + byte_t cr3a; + byte_t * vga_base = S3DC.vga_base; + SVGAMode * mode = (SVGAMode*)(mode_buf); + if (!mode || buf_size < sizeof (SVGAMode)) + { + return - sizeof (SVGAMode); + } + + if (mode->pixel_size) S3DC.bpp = mode->pixel_size; + if (mode->pixel_width) S3DC.width = mode->pixel_width; + if (mode->pixel_height) S3DC.height = mode->pixel_height; + if (!S3DC.logical_width) S3DC.logical_width = mode->pixel_width; + S3DC.display_start = 0; + + s3drv_unlock_regs (); + write_sr (vga_base, 0x01, read_sr (vga_base, 0x01) | 0x20); + for (i = 0; crtc_reg_defaults[i] != 0; i ++) + write16 (vga_base + CRTC_INDEX, crtc_reg_defaults[i]); + for (i = 0; seq_reg_defaults[i] != 0; i ++) + write16 (vga_base + SEQ_INDEX, seq_reg_defaults[i]); + if (restore_vga > 0) + { + write_cr (vga_base, 0x11, read_cr (vga_base, 0x11) & 0x7f); + for (i = 1; i < 4; i ++) + write_sr (vga_base, i+1, mode->seq_regs[i]); + for (i = 0; i < 0x0c; i ++) + write_cr (vga_base, i, mode->crtc_regs[i]); + for (i = 0x10; i < 0x19; i ++) + write_cr (vga_base, i, mode->crtc_regs[i]); + for (i = 0; i < 0x14; i ++) + write_ar (vga_base, i, mode->attr_regs[i]); + for (i = 0; i < 0x09; i ++) + write_gr (vga_base, i, mode->graph_regs[i]); + } + + write8 (vga_base + 0x3c2, 0x23); + write_sr (vga_base, 0x15, (mode->misc_ctrl & 0x10) | 2); + write8 (vga_base + 0x3c2, + ((mode->misc_output & 0x3f) | + (mode->misc_ctrl & 0xc0))); + if ((mode->misc_output & 0x0c) == 0x0c) + { + write_sr (vga_base, 0x12, mode->sr12); + write_sr (vga_base, 0x13, mode->sr13); + write_sr (vga_base, 0x29, mode->sr29); + } + + if (mode->refresh_rate != 0) + { + write_cr (vga_base, 0x43, mode->cr43); + write_cr (vga_base, 0x50, mode->cr50); + write_cr (vga_base, 0x51, mode->cr51); + write_cr (vga_base, 0x5d, mode->cr5d); + write_cr (vga_base, 0x5e, mode->cr5e); + write_cr (vga_base, 0x5e, mode->cr5f); + write_cr (vga_base, 0x66, mode->cr66); + write_cr (vga_base, 0x67, mode->cr67); + write_cr (vga_base, 0x31, mode->cr31); + write_cr (vga_base, 0x58, mode->cr58); + write_cr (vga_base, 0x69, mode->cr69); + } + + /* Turn off hardware graphics cursor */ + + write_cr (vga_base, 0x45, read_cr (vga_base, 0x45) & ~1); + cr3a = read_cr (vga_base, 0x3a); + + /* Set attribute controller for enhanced mode */ + if (mode->pixel_size < 8) cr3a &= ~0x10; + else cr3a |= 0x10; + write_cr (vga_base, 0x3a, cr3a); + + /* RAMDAC mask */ + write8 (vga_base + 0x3c6, 0xff); + + write_sr (vga_base, 0x01, mode->seq_regs[0]); + s3drv_lock_regs (); + + if (mode->cr66 & 1) + { + write32 (S3DC.mmio_base + 0x81c0, S3DC.display_start); + write32 (S3DC.mmio_base + 0x81c8, /* set stride */ + (S3DC.logical_width * S3DC.bpp + 7) >> 3); + write32 (S3DC.mmio_base + 0x8168, S3DC.display_start); + /* Need to write GBD twice after an engine reset */ + if (S3DC.global_bd) + { + write32 (S3DC.mmio_base + 0x816c, S3DC.global_bd); + write32 (S3DC.mmio_base + 0x816c, S3DC.global_bd); + } + } + + return 1; +} + + diff --git a/src/add-ons/accelerants/s3savage/s3drv.h b/src/add-ons/accelerants/s3savage/s3drv.h new file mode 100644 index 0000000000..f10dc1d255 --- /dev/null +++ b/src/add-ons/accelerants/s3savage/s3drv.h @@ -0,0 +1,118 @@ +/* + * Copyright 1999 Erdi Chen + */ + + +#ifndef __S3DRV_H__ +#define __S3DRV_H__ + + +#include "datatype.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + word_t disp; + word_t sync_start; + word_t sync_end; + word_t total; + word_t polarity; +} CrtcTiming; + +typedef struct +{ + dword_t dot_clock_khz; + CrtcTiming x; + CrtcTiming y; +} PixelTiming; + +typedef struct +{ + byte_t * mmio_base; + byte_t * vga_base; + byte_t * rom_base; + byte_t * fb_base; + word_t device_id; + int fb_size; + int vram_size; + dword_t bpp; + dword_t width; + dword_t height; + dword_t logical_width; + dword_t display_start; + dword_t global_bd; + word_t viewport_x; + word_t viewport_y; + int cursor_adjustment; +} S3DriverContext; + + +extern void s3drv_dpf (const char * format, ...); +extern S3DriverContext * s3drv_get_context (); +extern int s3drv_init (byte_t * mmio_base); +extern void s3drv_lock_regs (); +extern void s3drv_unlock_regs (); +extern int s3drv_detect_vram_mb_size ( + byte_t * vram_start, int max_mb); + +extern int s3drv_is_text_mode (); +extern int s3drv_save_font ( + byte_t * vram, byte_t * font_buf, int buf_size); +extern int s3drv_restore_font ( + byte_t * vram, byte_t * font_buf, int buf_size); +extern int s3drv_get_mode ( + PixelTiming * timing, int bpp, + byte_t * mode_buf, int buf_size); +#if 0 +extern int s3drv_get_gtf_mode ( + int width, int height, int depth, + int rrate, byte_t * mode_buf, + int buf_size); +#endif +extern int s3drv_get_bios_mode ( + int vesamode, int refresh_rate, + byte_t * mode_buf, int buf_size, + byte_t * rom); +extern int s3drv_save_mode ( + byte_t * mode_buf, int buf_size); +extern int s3drv_restore_mode ( + byte_t * mode_buf, int buf_size, + int restore_vga); + +extern int s3drv_display_is_off (); +extern void s3drv_turn_display_off (); +extern void s3drv_turn_display_on (); + +extern int s3drv_set_logical_width (int width); +extern int s3drv_adjust_viewport (int x, int y); +extern int s3drv_set_display_start (int offset); + +extern void s3drv_wait_for_vsync (); +extern void s3drv_wait_for_idle (); + +extern int s3drv_set_cursor_color ( + dword_t foreground, dword_t background); +extern int s3drv_load_cursor ( + byte_t * lfb, int offset, int width, int height, + byte_t * image, byte_t * mask); +extern void s3drv_move_cursor (int x, int y); +extern void s3drv_show_cursor (); +extern void s3drv_hide_cursor (); + +extern void s3drv_set_palette ( + int index, int red, + int green, int blue); +extern void s3drv_get_palette ( + int index, int *red, + int *green, int *blue); + +#ifdef __cplusplus +} +#endif + + +#endif /* __S3DRV_H__ */ diff --git a/src/add-ons/accelerants/s3savage/s3mmio.h b/src/add-ons/accelerants/s3savage/s3mmio.h new file mode 100644 index 0000000000..541f8c980b --- /dev/null +++ b/src/add-ons/accelerants/s3savage/s3mmio.h @@ -0,0 +1,133 @@ +/* + * Copyright 1999 Erdi Chen + */ + + +#ifndef __S3MMIO_H__ +#define __S3MMIO_H__ + +#include "datatype.h" + +#ifdef __STRICT_ANSI__ + +#undef __inline__ +#define __inline__ +#define read8(addr) (*(volatile byte_t*) (addr)) +#define read16(addr) (*(volatile word_t*) (addr)) +#define read32(addr) (*(volatile dword_t*) (addr)) +#define write8(addr, val) ((*(volatile byte_t*) (addr)) = (byte_t) (val)) +#define write16(addr, val) ((*(volatile word_t*) (addr)) = (word_t) (val)) +#define write32(addr, val) ((*(volatile dword_t*) (addr)) = (dword_t) (val)) + +#else + +static __inline__ byte_t read8 (byte_t * addr) +{ + return *(volatile byte_t*) addr; +} + +static __inline__ word_t read16 (byte_t * addr) +{ + return *(volatile word_t*) addr; +} + +static __inline__ dword_t read32 (byte_t *addr) +{ + return *(volatile dword_t*) addr; +} + +static __inline__ void write8 (byte_t * addr, byte_t val) +{ + *(volatile byte_t*) addr = val; +} + +static __inline__ void write16 (byte_t * addr, word_t val) +{ + *(volatile word_t*) addr = val; +} + +static __inline__ void write32 (byte_t * addr, dword_t val) +{ + *(volatile dword_t*) addr = val; +} + +#endif /* __STRICT_ANSI__ */ + + +static __inline__ byte_t read_sr (byte_t * vga_base, byte_t reg) +{ + write8 (vga_base + SEQ_INDEX, reg); + return read8 (vga_base + SEQ_DATA); +} + +static __inline__ void write_sr (byte_t * vga_base, byte_t reg, byte_t val) +{ + write16 (vga_base + SEQ_INDEX, (val << 8) | reg); +} + +static __inline__ byte_t read_cr (byte_t * vga_base, byte_t reg) +{ + write8 (vga_base + CRTC_INDEX, reg); + return read8 (vga_base + CRTC_DATA); +} + +static __inline__ void write_cr (byte_t * vga_base, byte_t reg, byte_t val) +{ + write16 (vga_base + CRTC_INDEX, (val << 8) | reg); +} + +static __inline__ byte_t read_gr (byte_t * vga_base, byte_t reg) +{ + write8 (vga_base + 0x3ce, reg); + return read8 (vga_base + 0x3cf); +} + +static __inline__ void write_gr (byte_t * vga_base, byte_t reg, byte_t val) +{ + write16 (vga_base + 0x3ce, (val << 8) | reg); +} + +static __inline__ byte_t read_ar (byte_t * vga_base, byte_t reg) +{ + byte_t value, index; + read8 (vga_base + 0x3da); + index = read8 (vga_base + 0x3c0); + write8 (vga_base + 0x3c0, reg); + value = read8 (vga_base + 0x3c1); + read8 (vga_base + 0x3da); + write8 (vga_base + 0x3c0, index); + return value; +} + +static __inline__ void write_ar (byte_t * vga_base, byte_t reg, byte_t val) +{ + byte_t index; + read8 (vga_base + 0x3da); + index = read8 (vga_base + 0x3c0); + write8 (vga_base + 0x3c0, reg); + write8 (vga_base + 0x3c0, val); + write8 (vga_base + 0x3c0, index); +} + +static __inline__ void s3_lock_regs (byte_t * vga_base) +{ + write_cr (vga_base, 0x11, read_cr (vga_base, 0x11) | 0x80); + write_cr (vga_base, 0x33, (0x50 | read_cr (vga_base, 0x33)) & ~2); + write_cr (vga_base, 0x38, 0x00); + write_cr (vga_base, 0x39, 0x00); + write_sr (vga_base, 0x08, 0x00); +} + +static __inline__ void s3_unlock_regs (byte_t * vga_base) +{ + write_sr (vga_base, 0x08, 0x06); + write_cr (vga_base, 0x38, 0x48); + write_cr (vga_base, 0x39, 0xa0); + write_cr (vga_base, 0x40, 0x01); + write_cr (vga_base, 0x11, read_cr (vga_base, 0x11) & 0x7f); + write_cr (vga_base, 0x33, (~0x50 & read_cr (vga_base, 0x33)) | 2); + write_cr (vga_base, 0x35, 0x00); +} + + +#endif /* __S3MMIO_H__ */ diff --git a/src/add-ons/accelerants/s3savage/s3vga.c b/src/add-ons/accelerants/s3savage/s3vga.c new file mode 100644 index 0000000000..62bb5d75bd --- /dev/null +++ b/src/add-ons/accelerants/s3savage/s3vga.c @@ -0,0 +1,271 @@ +/* + * Copyright 1999 Erdi Chen + */ + + +#include "datatype.h" +#include "s3drv.h" + +#define BASE_FREQ 14.318 + +static int pll_limits[] = +{ + 1, 511, /* min M, max M */ + 1, 127, /* min N, max N */ + 0, 4, /* min R, max R */ + 317500, 635000 /* min VCO, max VCO */ +}; + +typedef struct +{ + word_t h_display_end; + word_t h_sync_start; + word_t h_sync_end; + word_t h_total; + word_t h_polarity; + word_t v_display_end; + word_t v_sync_start; + word_t v_sync_end; + word_t v_total; + word_t v_polarity; + word_t h_blank_start; + word_t h_blank_end; + word_t v_blank_start; + word_t v_blank_end; +} CharTiming; + + +static int vga_convert_timing(int pixel_per_char, + CharTiming * char_timing, + PixelTiming * pixel_timing) +{ + int i, blank_offset; + char_timing->h_total = pixel_timing->x.total / pixel_per_char; + char_timing->h_sync_start = pixel_timing->x.sync_start / pixel_per_char; + char_timing->h_sync_end = pixel_timing->x.sync_end / pixel_per_char; + char_timing->h_display_end = pixel_timing->x.disp / pixel_per_char; +#if 0 + char_timing->h_blank_start = char_timing->h_display_end; + char_timing->h_blank_end = char_timing->h_sync_end + 1; +#else + blank_offset = (pixel_timing->x.disp * 3 + 100) / 200; + if (blank_offset < 2) blank_offset = 2; + char_timing->h_blank_start = char_timing->h_display_end + blank_offset; + char_timing->h_blank_end = char_timing->h_total - blank_offset; + i = char_timing->h_blank_end - char_timing->h_blank_start - 127; + if (i > 0) + { + char_timing->h_blank_start += (i + 1) / 2; + char_timing->h_blank_end -= i / 2; + } + char_timing->h_blank_end ++; +#endif + char_timing->v_total = pixel_timing->y.total; + char_timing->v_sync_start = pixel_timing->y.sync_start; + char_timing->v_sync_end = pixel_timing->y.sync_end; + char_timing->v_display_end = pixel_timing->y.disp; + char_timing->v_blank_start = char_timing->v_display_end + 7; + char_timing->v_blank_end = (char_timing->v_total + 7) - 0x10; + + char_timing->h_polarity = (pixel_timing->x.polarity ? 1 : 0) << 6; + char_timing->v_polarity = (pixel_timing->y.polarity ? 1 : 0) << 7; + + return 1; +} + +static int vga_get_mode (VGAMode * vgamode, + CharTiming * char_timing, + PixelTiming * pixel_timing) +{ + word_t line_compare; + word_t double_scan; + word_t row_width; + word_t mem_addr_size; + word_t pixel_per_addr; + + if (!vgamode || !pixel_timing) return 0; + + double_scan = 0; + line_compare = 0x3ff; + mem_addr_size = 4; + pixel_per_addr = 1; + row_width = 2 * pixel_timing->x.disp / (pixel_per_addr * mem_addr_size); + + char_timing->h_total -= 5; + char_timing->h_display_end --; + char_timing->v_total -= 2; + char_timing->v_display_end --; + char_timing->v_sync_start --; + char_timing->v_sync_end --; + + vgamode->misc_output = 3 | + (!char_timing->h_polarity) << 6 | + (!char_timing->v_polarity) << 7; + + vgamode->crtc_regs[0x00] = char_timing->h_total & 0xff; + vgamode->crtc_regs[0x01] = char_timing->h_display_end & 0xff; + vgamode->crtc_regs[0x02] = char_timing->h_blank_start & 0xff; + vgamode->crtc_regs[0x03] = 0x80 | (char_timing->h_blank_end & 0x1f); + vgamode->crtc_regs[0x04] = char_timing->h_sync_start & 0xff; + vgamode->crtc_regs[0x05] = ((char_timing->h_blank_end & 0x20) << 2) | + (char_timing->h_sync_end & 0x1f); + vgamode->crtc_regs[0x06] = char_timing->v_total & 0xff; + vgamode->crtc_regs[0x07] = ((char_timing->v_total & 0x100) >> 8) | + ((char_timing->v_display_end & 0x100) >> 7) | + ((char_timing->v_sync_start & 0x100) >> 6) | + ((char_timing->v_blank_start & 0x100) >> 5) | + ((line_compare & 0x100) >> 4) | + ((char_timing->v_total & 0x200) >> 4) | + ((char_timing->v_display_end & 0x200) >> 3) | + ((char_timing->v_sync_start & 0x200) >> 2); + vgamode->crtc_regs[0x08] = 0; + vgamode->crtc_regs[0x09] = (double_scan & 0x80) | + ((char_timing->v_blank_start & 0x200) >> 4) | + ((line_compare & 0x200) >> 3); + vgamode->crtc_regs[0x2a] = 0; + vgamode->crtc_regs[0x0b] = 0; + vgamode->crtc_regs[0x0c] = 0; + vgamode->crtc_regs[0x0d] = 0; + vgamode->crtc_regs[0x0e] = 0xff; + vgamode->crtc_regs[0x0f] = 0; + vgamode->crtc_regs[0x10] = char_timing->v_sync_start & 0xff; + vgamode->crtc_regs[0x11] = (char_timing->v_sync_end & 0x0f) | 0x80; + vgamode->crtc_regs[0x12] = char_timing->v_display_end & 0xff; + vgamode->crtc_regs[0x13] = row_width & 0xff; + vgamode->crtc_regs[0x14] = ((mem_addr_size > 3) ? 0x60 : 0); + vgamode->crtc_regs[0x15] = char_timing->v_blank_start & 0xff; + vgamode->crtc_regs[0x16] = char_timing->v_blank_end & 0xff; + vgamode->crtc_regs[0x17] = ((mem_addr_size == 1) ? 0x40 : 0x08) | 0xa3; + vgamode->crtc_regs[0x18] = line_compare & 0xff; + + return 1; +} + +/* -------------------------------------------------------------- + * 1, 511, min M, max M + * 1, 127, min N, max N + * 0, 4, min R, max R + * 317500, 635000 min VCO, max VCO + * --------------------------------------------------------------*/ +void __s3drv_encode_clock (long freq, int * pll_limits, + int * mdiv, int * ndiv, int * pll_r) +{ + double ffreq, ffreq_min, ffreq_max; + double div, diff, best_diff; + int m; + int n, r; + int best_n = 16 + 2; + int best_r = 2; + int best_m = 125 + 2; + + int min_m, max_m; + int min_n, max_n; + int min_r, max_r; + long freq_min, freq_max; + + min_m = pll_limits[0]; max_m = pll_limits[1]; + min_n = pll_limits[2]; max_n = pll_limits[3]; + min_r = pll_limits[4]; max_r = pll_limits[5]; + freq_min = pll_limits[6]; freq_max = pll_limits[7]; + + ffreq = freq / 1000.0 / BASE_FREQ; + ffreq_min = freq_min / 1000.0 / BASE_FREQ; + ffreq_max = freq_max / 1000.0 / BASE_FREQ; + + if (ffreq < ffreq_min / (1< ffreq_max / (1< max_m + 2) + continue; + div = (double)(m) / (double)(n); + + if ((div >= ffreq_min) && + (div <= ffreq_max)) + { + diff = ffreq - div / (1 << r); + if (diff < 0.0) + diff = -diff; + if (diff < best_diff) + { + best_diff = diff; + best_m = m; + best_n = n; + best_r = r; + } + } + } + } + + *ndiv = best_n - 2; + *pll_r = best_r; + *mdiv = best_m - 2; +} + +static void __s3drv_convert_clock (SVGAMode * svgamode, dword_t dot_clock_khz) +{ + int m, n, r; + + __s3drv_encode_clock (dot_clock_khz, & pll_limits[0], &m, &n, &r); + svgamode->sr12 = (n & 0x3f) | ((r & 0x3) << 6); + svgamode->sr13 = (m & 0xff); + svgamode->sr29 = (r & 0x04) | ((m & 0x100) >> 5) | ((n & 0x40) >> 2); +} + +int __s3drv_get_svga_mode(SVGAMode * svgamode, + PixelTiming * pixel_timing) +{ + int blank_width, sync_width; + dword_t dot_clock; + CharTiming char_timing; + + if (!svgamode || !pixel_timing) return 0; + + vga_convert_timing (8, & char_timing, pixel_timing); + vga_get_mode ((VGAMode*) svgamode, & char_timing, pixel_timing); + svgamode->misc_output |= 0x0c; + dot_clock = pixel_timing->dot_clock_khz; + if (!dot_clock) + { + svgamode->refresh_rate = 60; + dot_clock = pixel_timing->x.total * pixel_timing->y.total * 60 / 1000; + } + else + svgamode->refresh_rate = dot_clock * 1000 / + (pixel_timing->x.total * pixel_timing->y.total); + __s3drv_convert_clock (svgamode, dot_clock); + + blank_width = char_timing.h_blank_end - char_timing.h_blank_start; + sync_width = char_timing.h_sync_end - char_timing.h_sync_start; + svgamode->cr5d |= (char_timing.h_total & 0x100) >> 8 | + (char_timing.h_display_end & 0x100) >> 7 | + (char_timing.h_blank_start & 0x100) >> 6 | + (char_timing.h_sync_start & 0x100) >> 4; + if (blank_width > 32) svgamode->cr5d |= 0x20; + if (sync_width > 64) svgamode->cr5d |= 0x04; + + svgamode->cr5e = (char_timing.v_total & 0x400) >> 10 | + (char_timing.v_display_end & 0x400) >> 9 | + (char_timing.v_blank_start & 0x400) >> 8 | + (char_timing.v_sync_start & 0x400) >> 6; + + svgamode->cr5f |= (char_timing.h_total & 0x600) >> 9 | + (char_timing.h_display_end & 0x600) >> 7 | + (char_timing.h_blank_start & 0x600) >> 5 | + (char_timing.h_sync_start & 0x600) >> 3; + + return 1; +} diff --git a/src/add-ons/kernel/drivers/graphics/Jamfile b/src/add-ons/kernel/drivers/graphics/Jamfile index 0810dd48d0..e1b62d87bf 100644 --- a/src/add-ons/kernel/drivers/graphics/Jamfile +++ b/src/add-ons/kernel/drivers/graphics/Jamfile @@ -7,6 +7,7 @@ SubInclude HAIKU_TOP src add-ons kernel drivers graphics matrox ; SubInclude HAIKU_TOP src add-ons kernel drivers graphics neomagic ; SubInclude HAIKU_TOP src add-ons kernel drivers graphics nvidia ; SubInclude HAIKU_TOP src add-ons kernel drivers graphics radeon ; +SubInclude HAIKU_TOP src add-ons kernel drivers graphics s3savage ; SubInclude HAIKU_TOP src add-ons kernel drivers graphics tdfx ; SubInclude HAIKU_TOP src add-ons kernel drivers graphics skeleton ; SubInclude HAIKU_TOP src add-ons kernel drivers graphics vesa ; diff --git a/src/add-ons/kernel/drivers/graphics/s3savage/Jamfile b/src/add-ons/kernel/drivers/graphics/s3savage/Jamfile new file mode 100644 index 0000000000..8022492a15 --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/s3savage/Jamfile @@ -0,0 +1,16 @@ +SubDir HAIKU_TOP src add-ons kernel drivers graphics s3savage ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +UsePrivateHeaders [ FDirName graphics s3savage ] ; +UsePrivateHeaders [ FDirName graphics common ] ; +UsePrivateHeaders graphics kernel ; + +KernelAddon s3savage : + driver.c +; + +Package haiku-s3savage-cvs : + s3savage : + boot home config add-ons kernel drivers bin ; +PackageDriverSymLink haiku-s3savage-cvs : graphics s3savage ; diff --git a/src/add-ons/kernel/drivers/graphics/s3savage/LICENSE b/src/add-ons/kernel/drivers/graphics/s3savage/LICENSE new file mode 100644 index 0000000000..5311dbed10 --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/s3savage/LICENSE @@ -0,0 +1,27 @@ +This is a modified version of the XFree86 license. + +The binary and source form of this software is covered by the +following copyright/license. + +Copyright (C) 1999 Erdi Chen. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + diff --git a/src/add-ons/kernel/drivers/graphics/s3savage/driver.c b/src/add-ons/kernel/drivers/graphics/s3savage/driver.c new file mode 100644 index 0000000000..6f3e7a1136 --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/s3savage/driver.c @@ -0,0 +1,1011 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. +*/ +/* + * Copyright 1999 Erdi Chen + */ + +#include "DriverInterface.h" + +#include +#include +#include + +/* this is for the standardized portion of the driver API */ +/* currently only one operation is defined: B_GET_ACCELERANT_SIGNATURE */ +#include + +#include +#include +#include + +#define DEBUG 0 +#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)) + +#define MAX_DEVICES 8 + +/* 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 savage_interrupt(void *data); + +#if DEBUG > 0 +static int savagedump(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 +}; + +#define VENDOR_ID 0x5333 /* S3 Incorporated, S3 Graphics */ + +static uint16 savage_device_list[] = +{ + PCI_PID_SAVAGE3D, + PCI_PID_SAVAGE3DMV, + PCI_PID_SAVAGE4_2, + PCI_PID_SAVAGEMXMV, + PCI_PID_SAVAGEMX, + PCI_PID_SAVAGEIXMV, + PCI_PID_SAVAGEIX, + + PCI_PID_SAVAGE4_3, + PCI_PID_SAVAGE2000, + PCI_PID_PM133, + PCI_PID_KM133, + PCI_PID_PN133, + PCI_PID_KN133, + PCI_PID_KM266, + PCI_PID_PN266, + 0 +}; + +static struct +{ + uint16 vendor; + uint16 *devices; +} SupportedDevices[] = +{ + {VENDOR_ID, savage_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) +{ + ddprintf(("SKD: enter init_driver\n")); + /* 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("savagedump", savagedump, "dump SAVAGE kernel driver persistant data"); +#endif + ddprintf(("SKD: leave init_driver\n")); + 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("savagedump", savagedump); +#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] */ + char buffer[B_OS_NAME_LENGTH]; + shared_info *si = di->si; + uint32 tmpUlong; + pci_info *pcii = &(di->pcii); + uint32 fb_base = 0; + uint32 regs_base = 0; + uint fb_size = 0; + uint regs_size = 0; + + ddprintf(("SKD: enter map_device\n")); + /* enable memory mapped IO, disable VGA I/O */ + tmpUlong = get_pci(PCI_command, 4); + tmpUlong |= 0x00000002; + tmpUlong &= 0xfffffffe; + set_pci(PCI_command, 4, tmpUlong); + + /* enable ROM decoding */ + tmpUlong = get_pci(PCI_rom_base, 4); + tmpUlong |= 0x00000001; + set_pci(PCI_rom_base, 4, tmpUlong); + + switch (pcii->device_id) + { + case PCI_PID_SAVAGE3D: + case PCI_PID_SAVAGE3DMV: + case PCI_PID_SAVAGE4_2: + case PCI_PID_SAVAGEMXMV: + case PCI_PID_SAVAGEMX: + case PCI_PID_SAVAGEIXMV: + case PCI_PID_SAVAGEIX: + fb_base = di->pcii.u.h0.base_registers[0]; + fb_size = 16*1024*1024; + regs_base = di->pcii.u.h0.base_registers[0] + 0x1000000; + regs_size = 1024*1024; + break; + case PCI_PID_SAVAGE4_3: + case PCI_PID_SAVAGE2000: + case PCI_PID_PM133: + case PCI_PID_KM133: + case PCI_PID_PN133: + case PCI_PID_PN266: + case PCI_PID_KN133: + case PCI_PID_KM266: + fb_base = di->pcii.u.h0.base_registers[1]; + fb_size = 32*1024*1024; + regs_base = di->pcii.u.h0.base_registers[0]; + regs_size = 1024*1024; + break; + } + + /* map the areas */ + sprintf(buffer, "%04X_%04X_%02X%02X%02X regs", + di->pcii.vendor_id, di->pcii.device_id, + di->pcii.bus, di->pcii.device, di->pcii.function); + si->regs_area = map_physical_memory( + buffer, + (void *) regs_base, + regs_size, + B_ANY_KERNEL_ADDRESS, + 0, /* B_READ_AREA + B_WRITE_AREA, */ /* neither read nor write, to hide it from user space apps */ + (void **)&(di->regs)); + /* return the error if there was some problem */ + if (si->regs_area < 0) return si->regs_area; + + 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 *)di->pcii.u.h0.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 0 + if (si->rom_area < 0) + { + delete_area(si->regs_area); + si->regs_area = -1; + return si->rom_area; + } +#endif + + 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 *) fb_base, + fb_size, +#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 *) fb_base, + fb_size, +#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 *) fb_base; + /* in any case, return the result */ + ddprintf(("SKD: leave map_device\n")); + return si->fb_area; +} + +static void unmap_device(device_info *di) +{ + shared_info *si = di->si; + uint32 tmpUlong; + pci_info *pcii = &(di->pcii); + + ddprintf(("SKD: unmap_device(%08lx) begins...\n", (uint32)di)); + ddprintf(("\tregs_area: %ld\n\tfb_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(("SKD: unmap_device() ends.\n")); +} + +static void probe_devices(void) +{ + uint32 pci_index = 0; + uint32 count = 0; + device_info *di = pd->di; + + ddprintf(("SKD: enter probe_devices\n")); + /* 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; + + ddprintf(("SKD: checking pci index %ld, device 0x%04x/0x%04x\n", pci_index, di->pcii.vendor_id, di->pcii.device_id)); + /* 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 +savage_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 (1 /* 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 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 supposed 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 job */ + 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 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; + + /* a lie, but we have to start somewhen */ + + last_sync = system_time() - 8333; + + ddprintf(("SKD: 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(("SKD: fake_interrupt_thread_func ends with flags = 0x%08lx\n", *flags)); + + /* gotta return something */ + + return B_OK; +} +#endif + +#if DEBUG > 0 +static int savagedump(int argc, char **argv) { + int i; + + kprintf("SAVAGE 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); + } + kprintf("\n"); + } + 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; + thread_id thid; + thread_info thinfo; + 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; + result = B_OK; + + /* create a semaphore for vertical blank management */ + si->vblank = create_sem(0, di->name); + if (si->vblank < 0) { + result = si->vblank; + goto unmap; + } + + /* change the owner of the semaphores to the opener's team */ + /* this is required because apps can't aquire kernel semaphores */ + thid = find_thread(NULL); + get_thread_info(thid, &thinfo); + set_sem_owner(si->vblank, thinfo.team); + + /* assign local regs pointer for SAVAGExx() macros */ + regs = di->regs; + + /* disable and clear any pending interrupts */ + + /* 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, savage_interrupt, (void *)di, 0); + /* bail if we couldn't install the handler */ + if (result != B_OK) goto delete_the_sem; + } + +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(("SKD: 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; + vuint32 *regs = di->regs; + + 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; + + /* disable and clear any pending interrupts */ + *regs = *regs; /* CHANGE ME */ + + /* 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, savage_interrupt, di); + } + + /* delete the semaphores, ignoring any errors ('cause the owning team may have died on us) */ + delete_sem(si->vblank); + 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, "s3savage.accelerant"); + result = B_OK; + } + break; + + /* PRIVATE ioctl from here on */ + case SAVAGE_GET_PRIVATE_DATA: + { + savage_get_private_data *gpd = (savage_get_private_data *)buf; + if (gpd->magic == SAVAGE_PRIVATE_DATA_MAGIC) + { + gpd->shared_info_area = di->shared_area; + result = B_OK; + } + } + break; + case SAVAGE_GET_PCI: + { + savage_get_set_pci *gsp = (savage_get_set_pci *)buf; + if (gsp->magic == SAVAGE_PRIVATE_DATA_MAGIC) { + pci_info *pcii = &(di->pcii); + gsp->value = get_pci(gsp->offset, gsp->size); + result = B_OK; + } + } + break; + case SAVAGE_SET_PCI: + { + savage_get_set_pci *gsp = (savage_get_set_pci *)buf; + if (gsp->magic == SAVAGE_PRIVATE_DATA_MAGIC) { + pci_info *pcii = &(di->pcii); + set_pci(gsp->offset, gsp->size, gsp->value); + result = B_OK; + } + } + break; + case SAVAGE_RUN_INTERRUPTS: + { + savage_set_bool_state *ri = (savage_set_bool_state *)buf; + if (ri->magic == SAVAGE_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; + case SAVAGE_DPRINTF: + dprintf ((char*)buf); + break; + } + return result; +}