From 3ae79102aa705525742e57ccc7f34a25dfbbc601 Mon Sep 17 00:00:00 2001 From: Nathan Whitehorn Date: Thu, 24 Jun 2004 23:31:04 +0000 Subject: [PATCH] Added a TDFX driver on behalf of Andrew Drewke. There are some warnings I can't make go away in voodoo3_accelerant.c -- good luck with them. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@8147 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../private/graphics/tdfx/DriverInterface.h | 188 +++ src/add-ons/accelerants/Jamfile | 1 + src/add-ons/accelerants/tdfx/Acceleration.c | 93 ++ src/add-ons/accelerants/tdfx/Cursor.c | 114 ++ .../accelerants/tdfx/EngineManagment.c | 67 + .../accelerants/tdfx/GetAccelerantHook.c | 129 ++ src/add-ons/accelerants/tdfx/GetModeInfo.c | 97 ++ src/add-ons/accelerants/tdfx/GlobalData.c | 89 ++ src/add-ons/accelerants/tdfx/GlobalData.h | 45 + src/add-ons/accelerants/tdfx/InitAccelerant.c | 365 ++++++ src/add-ons/accelerants/tdfx/Jamfile | 21 + src/add-ons/accelerants/tdfx/Overlay.c | 317 +++++ .../accelerants/tdfx/ProposeDisplayMode.c | 388 ++++++ src/add-ons/accelerants/tdfx/SetDisplayMode.c | 254 ++++ src/add-ons/accelerants/tdfx/generic.h | 66 + .../accelerants/tdfx/voodoo3_accelerant.c | 756 ++++++++++++ .../accelerants/tdfx/voodoo3_accelerant.h | 82 ++ src/add-ons/accelerants/tdfx/voodoo3_io.h | 182 +++ src/add-ons/kernel/drivers/graphics/Jamfile | 1 + .../kernel/drivers/graphics/tdfx/Jamfile | 17 + .../kernel/drivers/graphics/tdfx/driver.c | 1096 +++++++++++++++++ 21 files changed, 4368 insertions(+) create mode 100644 headers/private/graphics/tdfx/DriverInterface.h create mode 100644 src/add-ons/accelerants/tdfx/Acceleration.c create mode 100644 src/add-ons/accelerants/tdfx/Cursor.c create mode 100644 src/add-ons/accelerants/tdfx/EngineManagment.c create mode 100644 src/add-ons/accelerants/tdfx/GetAccelerantHook.c create mode 100644 src/add-ons/accelerants/tdfx/GetModeInfo.c create mode 100644 src/add-ons/accelerants/tdfx/GlobalData.c create mode 100644 src/add-ons/accelerants/tdfx/GlobalData.h create mode 100644 src/add-ons/accelerants/tdfx/InitAccelerant.c create mode 100644 src/add-ons/accelerants/tdfx/Jamfile create mode 100644 src/add-ons/accelerants/tdfx/Overlay.c create mode 100644 src/add-ons/accelerants/tdfx/ProposeDisplayMode.c create mode 100644 src/add-ons/accelerants/tdfx/SetDisplayMode.c create mode 100644 src/add-ons/accelerants/tdfx/generic.h create mode 100644 src/add-ons/accelerants/tdfx/voodoo3_accelerant.c create mode 100644 src/add-ons/accelerants/tdfx/voodoo3_accelerant.h create mode 100644 src/add-ons/accelerants/tdfx/voodoo3_io.h create mode 100644 src/add-ons/kernel/drivers/graphics/tdfx/Jamfile create mode 100644 src/add-ons/kernel/drivers/graphics/tdfx/driver.c diff --git a/headers/private/graphics/tdfx/DriverInterface.h b/headers/private/graphics/tdfx/DriverInterface.h new file mode 100644 index 0000000000..c6b530b91b --- /dev/null +++ b/headers/private/graphics/tdfx/DriverInterface.h @@ -0,0 +1,188 @@ +/* + 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 + +#define MAXBUFFERS 8 + +typedef struct +{ + uint16 slopspace; + uint32 size; +} int_buf_info; + +typedef struct +{ + sem_id sem; + int32 ben; +} benaphore; + +#define INIT_BEN(x) x.sem = create_sem(0, "VOODOO "#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 VOODOO_PRIVATE_DATA_MAGIC 0x1234 /* a private driver rev, of sorts */ + +#define MAX_VOODOO_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 +{ + VOODOO_GET_PRIVATE_DATA = B_DEVICE_OP_CODES_END + 1, + VOODOO_GET_PCI, + VOODOO_SET_PCI, + VOODOO_DEVICE_NAME, + VOODOO_RUN_INTERRUPTS, + VOODOO_DPRINTF, + VOODOO_INB, + VOODOO_INW, + VOODOO_INL, + VOODOO_OUTB, + VOODOO_OUTW, + VOODOO_OUTL +}; + +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 */ + uint32 iobase; /* PCI IOBase */ + + 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 io_area; /* Mapped ROM's area_id */ + uint32 io; /* 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; + + struct + { + overlay_buffer myBuffer[MAXBUFFERS]; /* scaler input buffers */ + int_buf_info myBufInfo[MAXBUFFERS]; /* extra info on scaler input buffers */ + overlay_token myToken; /* scaler is free/in use */ + benaphore lock; /* for creating buffers and aquiring overlay unit routines */ + } overlay; + + 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 */ +} voodoo_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 */ +} voodoo_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 */ +} voodoo_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 */ +} voodoo_device_name; + +typedef struct +{ + uint32 port; + uint8 data8; + uint16 data16; + uint32 data32; +} voodoo_port_io; + +enum +{ + VOODOO_WAIT_FOR_VBLANK = (1 << 0) +}; + +#if defined(__cplusplus) +} +#endif + + +#endif diff --git a/src/add-ons/accelerants/Jamfile b/src/add-ons/accelerants/Jamfile index b46c8e6a81..edbbb08162 100644 --- a/src/add-ons/accelerants/Jamfile +++ b/src/add-ons/accelerants/Jamfile @@ -4,3 +4,4 @@ SubInclude OBOS_TOP src add-ons accelerants matrox ; SubInclude OBOS_TOP src add-ons accelerants neomagic ; SubInclude OBOS_TOP src add-ons accelerants nvidia ; SubInclude OBOS_TOP src add-ons accelerants radeon ; +SubInclude OBOS_TOP src add-ons accelerants tdfx ; diff --git a/src/add-ons/accelerants/tdfx/Acceleration.c b/src/add-ons/accelerants/tdfx/Acceleration.c new file mode 100644 index 0000000000..9db9701731 --- /dev/null +++ b/src/add-ons/accelerants/tdfx/Acceleration.c @@ -0,0 +1,93 @@ +/* + - PPC Port: Andreas Drewke (andreas_dr@gmx.de) + - Voodoo3Driver 0.02 (c) by Carwyn Jones (2002) + +*/ + +#include "GlobalData.h" +#include "generic.h" +#include "voodoo3_accelerant.h" + +void +SCREEN_TO_SCREEN_BLIT ( + engine_token *et, blit_params *list, uint32 count) +{ + uint32 stride, bpp; + stride = si->fbc.bytes_per_row; + bpp = voodoo3_bits_per_pixel(); + + while (count--) + { + list_packet_blit packet; + packet.src_x = list->src_left; + packet.src_y = list->src_top; + packet.dest_x = list->dest_left; + packet.dest_y = list->dest_top; + packet.width = list->width + 1; + packet.height = list->height + 1; + + voodoo3_screen_to_screen_blit(&packet, stride, bpp); + list ++; + } +} + +void +FILL_RECTANGLE ( + engine_token *et, uint32 color, fill_rect_params *list, uint32 count) +{ + uint32 stride, bpp; + stride = si->fbc.bytes_per_row; + bpp = voodoo3_bits_per_pixel(); + + while (count--) + { + list_packet packet; + packet.x = list->left; + packet.y = list->top; + packet.w = list->right - packet.x + 1; + packet.h = list->bottom - packet.y + 1; + voodoo3_fill_rect(&packet, color, stride, bpp); + list++; + } +} + +void +INVERT_RECTANGLE ( + engine_token *et, fill_rect_params *list, uint32 count) +{ + uint32 stride, bpp, color; + stride = si->fbc.bytes_per_row; + bpp = voodoo3_bits_per_pixel(); + color = 0; + + while (count--) + { + list_packet packet; + packet.x = list->left; + packet.y = list->top; + packet.w = list->right - packet.x + 1; + packet.h = list->bottom - packet.y + 1; + voodoo3_invert_rect(&packet, color, stride, bpp); + list++; + } +} + +void +FILL_SPAN ( + engine_token *et, uint32 color, uint16 *list, uint32 count) +{ + uint32 stride, bpp; + stride = si->fbc.bytes_per_row; + bpp = voodoo3_bits_per_pixel(); + + while (count--) + { + list_packet packet; + packet.y = *list ++; + packet.x = *list ++; + packet.w = *list - packet.x + 1; + packet.h = 1; + list ++; + voodoo3_fill_span(&packet, color, stride, bpp); + } +} diff --git a/src/add-ons/accelerants/tdfx/Cursor.c b/src/add-ons/accelerants/tdfx/Cursor.c new file mode 100644 index 0000000000..a8e9b6a531 --- /dev/null +++ b/src/add-ons/accelerants/tdfx/Cursor.c @@ -0,0 +1,114 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + - PPC Port: Andreas Drewke (andreas_dr@gmx.de) + - Voodoo3Driver 0.02 (c) by Carwyn Jones (2002) + +*/ + +#include "GlobalData.h" +#include "generic.h" +#include "voodoo3_accelerant.h" + +void set_cursor_colors(void); + +void set_cursor_colors(void) +{ + /* it's only called by the INIT_ACCELERANT() */ + voodoo3_set_cursor_colors(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; + voodoo3_set_cursor((uint8 *)framebuffer, 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 */ + voodoo3_move_cursor(x, y); +} + +void SHOW_CURSOR (bool is_visible) +{ + if (is_visible) + voodoo3_show_cursor(); + else + voodoo3_hide_cursor(); + /* record for our info */ + si->cursor.is_visible = is_visible; +} diff --git a/src/add-ons/accelerants/tdfx/EngineManagment.c b/src/add-ons/accelerants/tdfx/EngineManagment.c new file mode 100644 index 0000000000..5783a8eb72 --- /dev/null +++ b/src/add-ons/accelerants/tdfx/EngineManagment.c @@ -0,0 +1,67 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + - PPC Port: Andreas Drewke (andreas_dr@gmx.de) + - Voodoo3Driver 0.02 (c) by Carwyn Jones (2002) + +*/ + +#include "GlobalData.h" +#include "generic.h" + +static engine_token voodoo_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 = &voodoo_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) +{ + // 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) +{ + si->engine.last_idle = st->counter; + return B_OK; +} + diff --git a/src/add-ons/accelerants/tdfx/GetAccelerantHook.c b/src/add-ons/accelerants/tdfx/GetAccelerantHook.c new file mode 100644 index 0000000000..64935a36b0 --- /dev/null +++ b/src/add-ons/accelerants/tdfx/GetAccelerantHook.c @@ -0,0 +1,129 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + - PPC Port: Andreas Drewke (andreas_dr@gmx.de) + - Voodoo3Driver 0.02 (c) by Carwyn Jones (2002) + +*/ +#include "generic.h" + +status_t check_overlay_capability(uint32 feature); + +/* + +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); + + /* only export video overlay functions if card is capable of it */ + HOOK(OVERLAY_COUNT); + HOOK(OVERLAY_SUPPORTED_SPACES); + HOOK(OVERLAY_SUPPORTED_FEATURES); + HOOK(ALLOCATE_OVERLAY_BUFFER); + HOOK(RELEASE_OVERLAY_BUFFER); + HOOK(GET_OVERLAY_CONSTRAINTS); + HOOK(ALLOCATE_OVERLAY); + HOOK(RELEASE_OVERLAY); + HOOK(CONFIGURE_OVERLAY); + +#undef HOOK +#undef ZERO + } +/* +Return a null pointer for any feature we don't understand. +*/ + return 0; +} + +status_t check_overlay_capability(uint32 feature) +{ + /* setup logmessage text */ + switch (feature) + { + case B_OVERLAY_COUNT: + case B_OVERLAY_SUPPORTED_SPACES: + case B_OVERLAY_SUPPORTED_FEATURES: + case B_ALLOCATE_OVERLAY_BUFFER: + case B_RELEASE_OVERLAY_BUFFER: + case B_GET_OVERLAY_CONSTRAINTS: + case B_ALLOCATE_OVERLAY: + case B_RELEASE_OVERLAY: + case B_CONFIGURE_OVERLAY: + return B_OK; + default: + return B_ERROR; + + } +} diff --git a/src/add-ons/accelerants/tdfx/GetModeInfo.c b/src/add-ons/accelerants/tdfx/GetModeInfo.c new file mode 100644 index 0000000000..c2d0d63939 --- /dev/null +++ b/src/add-ons/accelerants/tdfx/GetModeInfo.c @@ -0,0 +1,97 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + - PPC Port: Andreas Drewke (andreas_dr@gmx.de) + - Voodoo3Driver 0.02 (c) by Carwyn Jones (2002) + +*/ + +#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*/B_ERROR; +} diff --git a/src/add-ons/accelerants/tdfx/GlobalData.c b/src/add-ons/accelerants/tdfx/GlobalData.c new file mode 100644 index 0000000000..6294810ead --- /dev/null +++ b/src/add-ons/accelerants/tdfx/GlobalData.c @@ -0,0 +1,89 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + - PPC Port: Andreas Drewke (andreas_dr@gmx.de) + - Voodoo3Driver 0.02 (c) by Carwyn Jones (2002) + +*/ +#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; + +area_id iobase_area; +vuint32 *iobase; + +#ifdef DEBUG +void dpf (const char * format, ...) +{ + char buffer[4096] = "VOODOO: "; + va_list args; + va_start (args, format); + vsprintf (buffer + 8, format, args); + ioctl (fd, VOODOO_DPRINTF, buffer, 0); + va_end (args); +} +#endif + +void outb(uint32 port, uint8 data) +{ + voodoo_port_io io; + io.port = port; + io.data8 = data; + ioctl(fd, VOODOO_OUTB, &io, 0); + return; +} + +void outw(uint32 port, uint16 data) +{ + voodoo_port_io io; + io.port = port; + io.data16 = data; + ioctl(fd, VOODOO_OUTW, &io, 0); + return; +} + +void outl(uint32 port, uint32 data) +{ + voodoo_port_io io; + io.port = port; + io.data32 = data; + ioctl(fd, VOODOO_OUTL, &io, 0); + return; +} + +uint8 inb(uint32 port) +{ + voodoo_port_io io; + io.port = port; + ioctl(fd, VOODOO_INB, &io, 0); + return(io.data8); +} + +uint16 inw(uint32 port) +{ + voodoo_port_io io; + io.port = port; + ioctl(fd, VOODOO_INW, &io, 0); + return(io.data16); +} + +uint32 inl(uint32 port) +{ + voodoo_port_io io; + io.port = port; + ioctl(fd, VOODOO_INL, &io, 0); + return(io.data32); +} diff --git a/src/add-ons/accelerants/tdfx/GlobalData.h b/src/add-ons/accelerants/tdfx/GlobalData.h new file mode 100644 index 0000000000..66ad783874 --- /dev/null +++ b/src/add-ons/accelerants/tdfx/GlobalData.h @@ -0,0 +1,45 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + - PPC Port: Andreas Drewke (andreas_dr@gmx.de) + - Voodoo3Driver 0.02 (c) by Carwyn Jones (2002) + +*/ + +#if !defined(GLOBALDATA_H) +#define GLOBALDATA_H + +#include +#include +#include + +extern int fd; +extern shared_info *si; +extern area_id shared_info_area; +extern vuint32 *regs; +extern area_id regs_area; +extern area_id iobase_area; +extern vuint32 *iobase; +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. */ +// #define DEBUG + +#ifdef DEBUG +#define ddpf(a) dpf a +extern void dpf (const char * format, ...); +#else +#define ddpf(a) +#endif +extern void outb(uint32 port, uint8 data); +extern void outw(uint32 port, uint16 data); +extern void outl(uint32 port, uint32 data); +extern uint8 inb(uint32 port); +extern uint16 inw(uint32 port); +extern uint32 inl(uint32 port); +#endif diff --git a/src/add-ons/accelerants/tdfx/InitAccelerant.c b/src/add-ons/accelerants/tdfx/InitAccelerant.c new file mode 100644 index 0000000000..db573cea50 --- /dev/null +++ b/src/add-ons/accelerants/tdfx/InitAccelerant.c @@ -0,0 +1,365 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + - PPC Port: Andreas Drewke (andreas_dr@gmx.de) + - Voodoo3Driver 0.02 (c) by Carwyn Jones (2002) + +*/ + +#include "GlobalData.h" +#include "generic.h" +#include "voodoo3_accelerant.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); + +static status_t init_common(int the_fd); + +static int tty = 0; +/* Initialization code shared between primary and cloned accelerants */ +static status_t init_common(int the_fd) +{ + status_t result; + voodoo_get_private_data gpd; + + /* memorize the file descriptor */ + fd = the_fd; + ddpf(("TDFXV3:init_common_called: %d times\n", tty)); + tty++; + + ddpf (("TDFXV3:init_common begin\n")); + /* set the magic number so the driver knows we're for real */ + gpd.magic = VOODOO_PRIVATE_DATA_MAGIC; + /* contact driver and get a pointer to the registers and shared data */ + result = ioctl(fd, VOODOO_GET_PRIVATE_DATA, &gpd, sizeof(gpd)); + if (result != B_OK) goto error0; + + /* clone the shared area for our use */ + shared_info_area = clone_area ( + "VOODOO 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 ( + "VOODOO 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; + } + // We don't use it here, we use it trough Kernel Driver + /* + iobase_area = clone_area( + "VOODOO io area", (void **)&iobase, B_ANY_ADDRESS, + B_READ_AREA | B_WRITE_AREA, si->io_area); + */ + + /* clone the framebuffer buffer for our use */ + fb_area = clone_area ( + "VOODOO 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; + } + ddpf (("TDFXV3:starting voodoo3_init\n")); + + voodoo3_init((uint8 *)regs, si->iobase); + /* all done */ + goto error0; + +error2: + delete_area(regs_area); +error1: + delete_area(shared_info_area); +error0: + ddpf (("TDFXV3: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 */ + + // delete_area(iobase_area); + + /* a little cheap paranoia */ + framebuffer = 0; + 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; + uint32 cnt; + /* 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 */ + ddpf (("TDFXV3:pixelclock 8: %li\n", si->pix_clk_max8)); + ddpf (("TDFXV3:pixelclock 16: %li\n", si->pix_clk_max16)); + ddpf (("TDFXV3:pixelclock 32: %li\n", si->pix_clk_max32)); + + voodoo3_set_monitor_defaults(); + si->mem_size = voodoo3_get_memory_size(); + ddpf (("TDFXV3:mem_size = %dMB\n", si->mem_size / 1024 / 1024)); + if (!si->mem_size) { + result=-1; + goto error0; + } + + /* + 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;// + 131072; + /* 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); + INIT_BEN(si->overlay.lock); + for (cnt = 0; cnt < MAXBUFFERS; cnt++) + { + /* make sure overlay buffers are 'marked' as being free */ + si->overlay.myBuffer[cnt].buffer = NULL; + si->overlay.myBuffer[cnt].buffer_dma = NULL; + } + /* make sure overlay unit is 'marked' as being free */ + si->overlay.myToken = NULL; + + /* initialize the engine synchronization variables */ + + /* count of issued parameters or commands */ + si->engine.last_idle = si->engine.count = 0; + + /* set the cursor colors. You may or may not have to do this, depending + on the device. */ + + voodoo3_init_cursor_address(); + set_cursor_colors(); + + /* ensure cursor state */ + SHOW_CURSOR(false); + /* a winner! */ + result = B_OK; + goto error0; + + /* free up the benaphore */ + DELETE_BEN(si->engine.lock); + DELETE_BEN(si->overlay.lock); + +error2: + /* + Clean up any resources allocated in your device specific initialization + code. + */ + + /* + 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_VOODOO_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) +{ + voodoo_device_name dn; + status_t result; + + /* call the kernel driver to get the device name */ + dn.magic = VOODOO_PRIVATE_DATA_MAGIC; + /* store the returned info directly into the passed buffer */ + dn.name = (char *)data; + result = ioctl(fd, VOODOO_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( + "VOODOO 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; + char * names[] = + { "Voodoo3" }; + + adi->version = 0x10000; + strcpy (adi->name, names[0]); + strcpy (adi->chipset, names[0]); + strcpy (adi->serial_no, "0"); + adi->memory = si->mem_size; + adi->dac_speed = 300; + + retval = B_OK; + return retval; +} + diff --git a/src/add-ons/accelerants/tdfx/Jamfile b/src/add-ons/accelerants/tdfx/Jamfile new file mode 100644 index 0000000000..46eeba03b2 --- /dev/null +++ b/src/add-ons/accelerants/tdfx/Jamfile @@ -0,0 +1,21 @@ +SubDir OBOS_TOP src add-ons accelerants tdfx ; + +UsePrivateHeaders graphics ; +UsePrivateHeaders [ FDirName graphics tdfx ] ; + + +Addon tdfx.accelerant : accelerants : + voodoo3_accelerant.c + Acceleration.c + Cursor.c + EngineManagment.c + GetAccelerantHook.c + GetModeInfo.c + GlobalData.c + InitAccelerant.c + ProposeDisplayMode.c + SetDisplayMode.c + Overlay.c +; + +Depends tdfx.accelerant : tdfx.driver ; diff --git a/src/add-ons/accelerants/tdfx/Overlay.c b/src/add-ons/accelerants/tdfx/Overlay.c new file mode 100644 index 0000000000..08d418a5e4 --- /dev/null +++ b/src/add-ons/accelerants/tdfx/Overlay.c @@ -0,0 +1,317 @@ +/* Overlay.c Framwork Taken from Rudolf Cornelissen (Open)BeOS MGA Driver + 05/09-2002 V0.13 beta1 */ + +#include "GlobalData.h" +#include "generic.h" +#include "voodoo3_accelerant.h" + +static uint32 overlay_colorspaces [] = {(uint32)B_YCbCr422, +#if defined(__POWERPC__) + (uint32)B_RGB16_BIG, +#else + (uint32)B_RGB16, +#endif + (uint32)B_NO_COLOR_SPACE }; + + +// This method is never used AFAIK though it *is* exported on R5.0.3 and DANO. +// Does someone know howto invoke it? +// apparantly overlay count should report the number of 'overlay units' on the card + +uint32 OVERLAY_COUNT(const display_mode *dm) { + if (dm == NULL) + ddpf( ("OVERLAY_COUNT:No DM given\n")); + return 1; +} + +// This method is never used AFAIK though it *is* exported on R5.0.3 and DANO. +// Does someone know howto invoke it? +const uint32 *OVERLAY_SUPPORTED_SPACES(const display_mode *dm) { + if (dm == NULL) { + ddpf (("OVERLAY_SUPPORTED_SPACES:No DM given\n")); + return NULL; + } + /* return a B_NO_COLOR_SPACE terminated list */ + return &overlay_colorspaces[0]; +} + +// This method is never used AFAIK. On R5.0.3 and DANO it is not even exported! +uint32 OVERLAY_SUPPORTED_FEATURES(uint32 a_color_space) { + switch (a_color_space){ + default: + return + ( B_OVERLAY_COLOR_KEY | + B_OVERLAY_HORIZONTAL_FILTERING | + B_OVERLAY_VERTICAL_FILTERING ); + } +} + +const overlay_buffer *ALLOCATE_OVERLAY_BUFFER(color_space cs, uint16 width, uint16 height) { + + int offset = 0; // used to determine next buffer to create + uint32 adress, adress2, temp32; // used to calculate buffer adresses + uint32 oldsize = 0; // used to 'squeeze' new buffers between already existing ones + int cnt; + + + /* acquire the shared benaphore */ + AQUIRE_BEN(si->overlay.lock) + + /* find first empty slot (room for another buffer?) */ + for (offset = 0; offset < MAXBUFFERS; offset++) + if (si->overlay.myBuffer[offset].buffer == NULL) break; + + /* I just could make a lookup on overlay_colorspaces instead but its overhead I thing */ + + if (offset < MAXBUFFERS) { + switch (cs) { + case B_YCbCr422: +#if defined(__POWERPC__) + case B_RGB16_BIG: +#else + case B_RGB16: +#endif + si->overlay.myBuffer[offset].width = width; + si->overlay.myBuffer[offset].bytes_per_row = 2 * si->overlay.myBuffer[offset].width; + if (si->overlay.myBuffer[offset].width > 2048) { + // release the shared benaphore + RELEASE_BEN(si->overlay.lock) + return NULL; + } + break; + default: + // release the shared benaphore */ + RELEASE_BEN(si->overlay.lock) + return NULL; + } + + // store slopspace (in pixels) for each bitmap for use by 'overlay unit' (BES) + si->overlay.myBufInfo[offset].slopspace = si->overlay.myBuffer[offset].width - width; + + si->overlay.myBuffer[offset].space = cs; + si->overlay.myBuffer[offset].height = height; + + // we define the overlay buffers to reside 'in the back' of the cards RAM + + // calculate first free RAM adress in card: + adress2 = (((uint32)((uint8*)si->fbc.frame_buffer)) + + (si->fbc.bytes_per_row * si->dm.virtual_height)); + // calculate 'preliminary' buffer size including slopspace + oldsize = si->overlay.myBufInfo[offset].size; + si->overlay.myBufInfo[offset].size = + si->overlay.myBuffer[offset].bytes_per_row * si->overlay.myBuffer[offset].height; + + // calculate virtual memory adress that would be needed for a new bitmap + + adress = (((uint32)((uint8*)si->framebuffer)) + (si->mem_size)); + for (cnt = 0; cnt <= offset; cnt++) adress -= si->overlay.myBufInfo[cnt].size; + + // the > G200 scalers require buffers to be aligned to 16 byte pages cardRAM offset, G200 can do with + // 8 byte pages cardRAM offset. Compatible settings used, has no real downside consequences here + + // Check if we need to modify the buffers starting adress and thus the size + // calculate 'would be' cardRAM offset + temp32 = (adress - ((uint32)((vuint32 *)si->framebuffer))); + + // check if it is aligned // + if (temp32 != (temp32 & ~3)) { + // update the (already calculated) buffersize to get it aligned + si->overlay.myBufInfo[offset].size += (temp32 - (temp32 & ~3)); + // update the (already calculated) adress to get it aligned + adress -= (temp32 - (temp32 & ~3)); + } + + // First check now if buffer to be defined is 'last one' in memory (speaking backwards): + // this is done to prevent a large buffer getting created in the space a small buffer + // occupied earlier, if not all buffers created were deleted. + + for (cnt = offset; cnt < MAXBUFFERS; cnt++) { + if (si->overlay.myBuffer[cnt].buffer != NULL) { + // Check if the new buffer would fit into the space the single old one used here + if (si->overlay.myBufInfo[offset].size <= oldsize) { + // It does, so we reset to the old size and adresses to prevent the space from shrinking + // if we get here again... + adress -= (oldsize - si->overlay.myBufInfo[offset].size); + si->overlay.myBufInfo[offset].size = oldsize; + // force exiting the FOR loop + cnt = MAXBUFFERS; + } else { + // Reset to the old size to prevent the space from 'growing' if we get here again... + si->overlay.myBufInfo[offset].size = oldsize; + // release the shared benaphore + RELEASE_BEN(si->overlay.lock) + return NULL; + } + } + } + // check if we have enough space to setup this new bitmap + // (preventing overlap of desktop RAMspace & overlay bitmap RAMspace here) + if (adress < adress2) { + // nope, sorry + RELEASE_BEN(si->overlay.lock) + return NULL; + } + // continue buffer setup + si->overlay.myBuffer[offset].buffer = (void *) adress; + + // calculate physical memory adress (for dma use) + adress = (((uint32)((uint8*)si->framebuffer_pci)) + (si->mem_size)); + for (cnt = 0; cnt <= offset; cnt++) + adress -= si->overlay.myBufInfo[cnt].size; + // this adress is already aligned to the scaler's requirements (via the already modified sizes) + + si->overlay.myBuffer[offset].buffer_dma = (void *) adress; + + // release the shared benaphore + RELEASE_BEN(si->overlay.lock) + return &si->overlay.myBuffer[offset]; + } else { + // sorry, no more room for buffers */ + RELEASE_BEN(si->overlay.lock) + return NULL; + } +} + +status_t RELEASE_OVERLAY_BUFFER(const overlay_buffer *ob) { + int offset = 0; + if (ob != NULL) { + //Find the buffer + for (offset = 0; offset < MAXBUFFERS; offset++) + if (si->overlay.myBuffer[offset].buffer == ob->buffer) break; + + if (offset < MAXBUFFERS) { // delete current buffer + si->overlay.myBuffer[offset].buffer = NULL; + si->overlay.myBuffer[offset].buffer_dma = NULL; + + return B_OK; + } else { + return B_ERROR; + } + } else { + return B_ERROR; + } +} + +status_t GET_OVERLAY_CONSTRAINTS (const display_mode *dm, const overlay_buffer *ob, overlay_constraints *oc) { + + int offset = 0; + // Check for NULL Pointers + if ((dm == NULL) || (ob == NULL) || (oc == NULL)) { + return B_ERROR; + } + + // find the buffer + for (offset = 0; offset < MAXBUFFERS; offset++) + if (si->overlay.myBuffer[offset].buffer == ob->buffer) break; + + if (offset < MAXBUFFERS) { + // scaler input (values are in pixels) + oc->view.h_alignment = 3; + oc->view.v_alignment = 3; + + switch (ob->space) { + default: + // we should not be here, but set the worst-case value just to be safe anyway + oc->view.width_alignment = 3; + break; + } + + oc->view.height_alignment = 3; + oc->view.width.min = 1; + oc->view.height.min = 2; // two fields + oc->view.width.max = ob->width; + oc->view.height.max = ob->height; + + // scaler output restrictions + oc->window.h_alignment = 3; + oc->window.v_alignment = 3; + oc->window.width_alignment = 3; + oc->window.height_alignment = 3; + oc->window.width.min = 2; + if (dm->virtual_width > 2048) oc->window.width.max = 2048; else + oc->window.width.max = dm->virtual_width; + + oc->window.height.min = 2; + + if (dm->virtual_height > 2048) oc->window.height.max = 2048; else + oc->window.height.max = dm->virtual_height; + + oc->h_scale.min = 1.0f; + oc->h_scale.max = 4.0f; + oc->v_scale.min = 1.0f; + oc->v_scale.max = 4.0f; + return B_OK; + } else { + return B_ERROR; + } +} + +overlay_token ALLOCATE_OVERLAY(void) +{ + uint32 tmpToken; + // come up with a token + tmpToken = 0x12345678; + + // acquire the shared benaphore + AQUIRE_BEN(si->overlay.lock) + // overlay unit already in use? + if (si->overlay.myToken == NULL) { + // overlay unit is available + si->overlay.myToken = &tmpToken; + // release the shared benaphore + RELEASE_BEN(si->overlay.lock) + return si->overlay.myToken; + } else { + RELEASE_BEN(si->overlay.lock) + return NULL; + } +} + +status_t RELEASE_OVERLAY(overlay_token ot) { + + // is this call for real? + if ((ot == NULL) || (si->overlay.myToken == NULL) || (ot != si->overlay.myToken)) { + return B_ERROR; + } else { + // Do really something here! + voodoo3_stop_overlay(); + si->overlay.myToken = NULL; + return B_OK; + } +} + +status_t CONFIGURE_OVERLAY (overlay_token ot, const overlay_buffer *ob, const overlay_window *ow, const overlay_view *ov) { + + int offset = 0; // used for buffer index + + + // in BeOS R5.0.3 (maybe DANO works different): + // ov' is the output size on the desktop: does not change if clipped. + // h_start and v_start are always 0, width and heigth are the size of the window. + // Because the width and height can also be found in '*ow', '*ov' is not used. + + if ((ow == NULL) || (ov == NULL)) { + return B_OK; + } + + if (ob == NULL) { + return B_ERROR; + } + + // is this call done by the app that owns us? */ + if ((ot == NULL) || (si->overlay.myToken == NULL) || (ot != si->overlay.myToken)) { + // nope, abort // + return B_ERROR; + } else { + // call is for real + for (offset = 0; offset < MAXBUFFERS; offset++) + if (si->overlay.myBuffer[offset].buffer == ob->buffer) break; + + if (offset < MAXBUFFERS) { + voodoo3_display_overlay(ow, ob, ov); + return B_OK; + } else { + return B_ERROR; + } + } +} diff --git a/src/add-ons/accelerants/tdfx/ProposeDisplayMode.c b/src/add-ons/accelerants/tdfx/ProposeDisplayMode.c new file mode 100644 index 0000000000..fd3e496b4f --- /dev/null +++ b/src/add-ons/accelerants/tdfx/ProposeDisplayMode.c @@ -0,0 +1,388 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + - PPC Port: Andreas Drewke (andreas_dr@gmx.de) + - Voodoo3Driver 0.02 (c) by Carwyn Jones (2002) + +*/ +#include "GlobalData.h" +#include "generic.h" + +status_t create_mode_list(void); + +#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: + case B_RGB15_BIG: + case B_RGB16_BIG: + limit_clock = si->pix_clk_max16; + row_bytes = 2; + break; + case B_RGB32: + case B_RGB32_BIG: + 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_RGB16_LITTLE, B_RGB32_LITTLE}; +#else +// We could replace LITTLE with BIG + color_space spaces[] = + {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 ( + "VOODOO 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/tdfx/SetDisplayMode.c b/src/add-ons/accelerants/tdfx/SetDisplayMode.c new file mode 100644 index 0000000000..e99aca7d8a --- /dev/null +++ b/src/add-ons/accelerants/tdfx/SetDisplayMode.c @@ -0,0 +1,254 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + - PPC Port: Andreas Drewke (andreas_dr@gmx.de) + - Voodoo3Driver 0.02 (c) by Carwyn Jones (2002) + +*/ + +#include "GlobalData.h" +#include "generic.h" +#include +#include "voodoo3_accelerant.h" +#include + +/* + Enable/Disable interrupts. Just a wrapper around the + ioctl() to the kernel driver. + */ +static void interrupt_enable(bool flag) +{ + status_t result; + voodoo_set_bool_state sbs; + + /* set the magic number so the driver knows we're for real */ + sbs.magic = VOODOO_PRIVATE_DATA_MAGIC; + sbs.do_it = flag; + /* contact driver and get a pointer to the registers and shared data */ + result = ioctl(fd, VOODOO_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; +} + + +static void do_set_display_mode(display_mode *dm) +{ + uint32 bpp; + /* disable interrupts using the kernel driver */ + interrupt_enable(false); + + bpp = calcBitsPerPixel (dm->space); + voodoo_set_desktop_regs(bpp, dm); + /* enable interrupts using the kernel driver */ + interrupt_enable(true); + si->fbc.bytes_per_row = (dm->virtual_width * bpp + 7) / 8; +} + +/* + 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 */ + 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. + */ + while(count--) { + uint32 color = ((color_data[0] << 16) | (color_data[1] << 8) | (color_data[2])); + voodoo3_set_palette(first++, color); + color_data += 3; + } +} + + +/* 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/tdfx/generic.h b/src/add-ons/accelerants/tdfx/generic.h new file mode 100644 index 0000000000..a021a1cbf5 --- /dev/null +++ b/src/add-ons/accelerants/tdfx/generic.h @@ -0,0 +1,66 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + - PPC Port: Andreas Drewke (andreas_dr@gmx.de) + - Voodoo3Driver 0.02 (c) by Carwyn Jones (2002) + +*/ + +#if !defined(GENERIC_H) +#define GENERIC_H + +#include +#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); + +/* video_overlay */ +uint32 OVERLAY_COUNT(const display_mode *dm); +const uint32 *OVERLAY_SUPPORTED_SPACES(const display_mode *dm); +uint32 OVERLAY_SUPPORTED_FEATURES(uint32 a_color_space); +const overlay_buffer *ALLOCATE_OVERLAY_BUFFER(color_space cs, uint16 width, uint16 height); +status_t RELEASE_OVERLAY_BUFFER(const overlay_buffer *ob); +status_t GET_OVERLAY_CONSTRAINTS(const display_mode *dm, const overlay_buffer *ob, overlay_constraints *oc); +overlay_token ALLOCATE_OVERLAY(void); +status_t RELEASE_OVERLAY(overlay_token ot); +status_t CONFIGURE_OVERLAY(overlay_token ot, const overlay_buffer *ob, const overlay_window *ow, const overlay_view *ov); + +#endif diff --git a/src/add-ons/accelerants/tdfx/voodoo3_accelerant.c b/src/add-ons/accelerants/tdfx/voodoo3_accelerant.c new file mode 100644 index 0000000000..1a72cc5a04 --- /dev/null +++ b/src/add-ons/accelerants/tdfx/voodoo3_accelerant.c @@ -0,0 +1,756 @@ +/* + - PPC Port: Andreas Drewke (andreas_dr@gmx.de) + - Voodoo3Driver 0.02 (c) by Carwyn Jones (2002) + +*/ +#include +#include "voodoo3_io.h" +#include "voodoo3_accelerant.h" +#include "GlobalData.h" +#include + +static Voodoo3Accel accel = { 0, 0, 0, 0, 0}; +#define abs(x) (x<0?-x:x) + + +static __inline__ void write_crt_registers(uint8 reg, uint8 val) +{ + outw(accel.io_base + CRTC_INDEX - 0x300, (val << 8) | reg); +} + +static __inline__ void write_seq_registers(uint8 reg, uint8 val) +{ + outw(accel.io_base + SEQ_INDEX - 0x300, (val << 8) | reg); +} + +static __inline__ void write_graph_registers(uint8 reg, uint8 val) +{ + outw(accel.io_base + GRA_INDEX - 0x300, (val << 8) | reg); +} + +static __inline__ void write_attr_registers(uint8 reg, uint8 val) +{ + uint8 index; + inb (accel.io_base + IS1_R - 0x300); + index = inb(accel.io_base + ATT_IW - 0x300); + outb(accel.io_base + ATT_IW - 0x300, reg); + outb(accel.io_base + ATT_IW - 0x300, val); + outb(accel.io_base + ATT_IW - 0x300, index); +} + +static inline void vga_outb(unsigned long reg, unsigned char val) +{ + outb(accel.io_base + reg - 0x300, val); +} + +static inline void voodoo3_write32(unsigned int reg, unsigned long val) +{ + PCI_MEM_WR_32(accel.regs_base + reg, val); +} + +static inline uint32 voodoo3_read32(unsigned int reg) { + return PCI_MEM_RD_32(accel.regs_base + reg); +} + +static inline void voodoo3_make_room(unsigned int len) { + while((voodoo3_read32(STATUS) & 0x1f) < len); +} + +static void voodoo3_initialize() +{ + unsigned long vgainit0 = 0; + unsigned long vidcfg = 0; + unsigned long cpp = 16; + vgainit0 = + VGAINIT0_8BIT_DAC | + VGAINIT0_EXT_ENABLE | + VGAINIT0_WAKEUP_3C3 | + VGAINIT0_ALT_READBACK | + VGAINIT0_EXTSHIFTOUT; + + vidcfg = + VIDCFG_VIDPROC_ENABLE | + VIDCFG_DESK_ENABLE | + ((cpp - 1) << VIDCFG_PIXFMT_SHIFT) | + (cpp != 1 ? VIDCFG_CLUT_BYPASS : 0); + + voodoo3_make_room(2); + voodoo3_write32(VGAINIT0, vgainit0); + voodoo3_write32(VIDPROCCFG, vidcfg); + voodoo3_wait_idle(); +} + +int voodoo3_init(uint8 *registers_base, uint32 io_base) +{ + if(!registers_base) return 0; + accel.regs_base = registers_base; + accel.io_base = io_base; + voodoo3_initialize(); + return 1; +} + +uint32 voodoo3_bits_per_pixel() +{ + return accel.bpp; +} + +void voodoo3_set_monitor_defaults() +{ + MonitorRegs m; + MonitorRegs *mode; + int i; + + memset(&m, 0, sizeof(MonitorRegs)); + + mode = &m; + mode->crtc_regs[0] = 159; + mode->crtc_regs[1] = 127; + mode->crtc_regs[2] = 127; + mode->crtc_regs[3] = 131; + mode->crtc_regs[4] = 129; + + mode->crtc_regs[5] = 141; + mode->crtc_regs[6] = 30; + mode->crtc_regs[7] = 245; + mode->crtc_regs[8] = 0; + mode->crtc_regs[9] = 96; + + mode->crtc_regs[10] = 0; + mode->crtc_regs[11] = 0; + mode->crtc_regs[12] = 0; + mode->crtc_regs[13] = 0; + mode->crtc_regs[14] = 0; + mode->crtc_regs[15] = 0; + + mode->crtc_regs[16] = 1; + mode->crtc_regs[17] = 36; + mode->crtc_regs[18] = 255; + mode->crtc_regs[19] = 127; + mode->crtc_regs[20] = 0; + mode->crtc_regs[21] = 255; + + mode->crtc_regs[22] = 31; + mode->crtc_regs[23] = 128; + mode->crtc_regs[24] = 255; + + vga_outb(MISC_W, 0x0f); + + for (i = 0; i < 0x19; i ++) + write_crt_registers(i, mode->crtc_regs[i]); + +} + +void voodoo3_wait_idle() +{ + int i = 0; + + voodoo3_make_room(1); + voodoo3_write32(COMMAND_3D, COMMAND_3D_NOP); + + while(1) { + i = (voodoo3_read32(STATUS) & STATUS_BUSY) ? 0 : i + 1; + if(i == 3) break; + } +} + +#define REFFREQ 14318.18 + +static int +voodoo3_calc_pll(int freq, int *f_out, int isBanshee) { + int m, n, k, best_m, best_n, best_k, f_cur, best_error; + int minm, maxm; + + best_error=freq; + best_n=best_m=best_k=0; + if (isBanshee) { + minm=24; + maxm=24; + } else { + minm=1; + maxm=57; /* This used to be 64, alas it seems the last 8 (funny that ?) + * values cause jittering at lower resolutions. I've not done + * any calculations to what the adjustment affects clock ranges, + * but I can still run at 1600x1200@75Hz */ + } + for (n=1; n<256; n++) { + f_cur=REFFREQ*(n+2); + if (f_curtiming.h_display >> 3) - 1; + horizontal_display_end = (dm->timing.h_display >> 3) - 1; + horizontal_sync_start = (dm->timing.h_sync_start >> 3) - 1; + horizontal_sync_end = (dm->timing.h_sync_end >> 3) - 1; + horizontal_total = (dm->timing.h_total >> 3) - 1; + horizontal_blanking_start = horizontal_display_end; + horizontal_blanking_end = horizontal_total; + + vertical_display_enable_end = dm->timing.v_display - 1; + vertical_sync_start = dm->timing.v_sync_start;// - 1; + vertical_sync_end = dm->timing.v_sync_end;// - 1; + vertical_total = dm->timing.v_total - 2; + vertical_blanking_start = vertical_display_enable_end; + vertical_blanking_end = vertical_total; + + mode->misc = + 0x0f | + (dm->timing.h_display < 400 ? 0xa0 : + dm->timing.h_display < 480 ? 0x60 : + dm->timing.h_display < 768 ? 0xe0 : 0x20); + + mode->seq_regs[0] = 3; + mode->seq_regs[1] = 1; + mode->seq_regs[2] = 8; + mode->seq_regs[3] = 0; + mode->seq_regs[4] = 6; + + // crtc regs start + mode->crtc_regs[0] = horizontal_total - 4; + mode->crtc_regs[1] = horizontal_display_end; + mode->crtc_regs[2] = horizontal_blanking_start; + mode->crtc_regs[3] = 0x80 | (horizontal_blanking_end & 0x1f); + mode->crtc_regs[4] = horizontal_sync_start; + + mode->crtc_regs[5] = ((horizontal_blanking_end & 0x20) << 2) | (horizontal_sync_end & 0x1f); + mode->crtc_regs[6] = vertical_total; + mode->crtc_regs[7] = ((vertical_sync_start & 0x200) >> 2) | + ((vertical_display_enable_end & 0x200) >> 3) | + ((vertical_total & 0x200) >> 4) | + 0x10 | + ((vertical_blanking_start & 0x100) >> 5) | + ((vertical_sync_start & 0x100) >> 6) | + ((vertical_display_enable_end & 0x100) >> 7) | + ((vertical_total & 0x100) >> 8); + + mode->crtc_regs[8] = 0; + mode->crtc_regs[9] = 0x40 | + ((vertical_blanking_start & 0x200) >> 4); + + mode->crtc_regs[10] = 0; + mode->crtc_regs[11] = 0; + mode->crtc_regs[12] = 0; + mode->crtc_regs[13] = 0; + mode->crtc_regs[14] = 0; + mode->crtc_regs[15] = 0; + + mode->crtc_regs[16] = vertical_sync_start; + mode->crtc_regs[17] = (vertical_sync_end & 0x0f) | 0x20; + mode->crtc_regs[18] = vertical_display_enable_end; + mode->crtc_regs[19] = wd; // CRTC offset + mode->crtc_regs[20] = 0; + mode->crtc_regs[21] = vertical_blanking_start; + + mode->crtc_regs[22] = vertical_blanking_end + 1; + mode->crtc_regs[23] = 128; + mode->crtc_regs[24] = 255; + + // attr regs start + mode->attr_regs[0] = 0; + mode->attr_regs[1] = 0; + mode->attr_regs[2] = 0; + mode->attr_regs[3] = 0; + mode->attr_regs[4] = 0; + mode->attr_regs[5] = 0; + + mode->attr_regs[6] = 0; + mode->attr_regs[7] = 0; + mode->attr_regs[8] = 0; + mode->attr_regs[9] = 0; + mode->attr_regs[10] = 0; + mode->attr_regs[11] = 0; + + mode->attr_regs[12] = 0; + mode->attr_regs[13] = 0; + mode->attr_regs[14] = 0; + mode->attr_regs[15] = 0; + mode->attr_regs[16] = 1; + mode->attr_regs[17] = 0; + mode->attr_regs[18] = 15; + mode->attr_regs[19] = 0; + // attr regs end + + // graph regs start + mode->graph_regs[0] = 159; + mode->graph_regs[1] = 127; + mode->graph_regs[2] = 127; + mode->graph_regs[3] = 131; + mode->graph_regs[4] = 130; + mode->graph_regs[5] = 142; + mode->graph_regs[6] = 30; + mode->graph_regs[7] = 245; + mode->graph_regs[8] = 0; + + vga_outb(MISC_W, mode->misc | 0x01); + + for(i = 0; i < 5; i++) + write_seq_registers(i, mode->seq_regs[i]); + for (i = 0; i < 0x19; i ++) + write_crt_registers(i, mode->crtc_regs[i]); + for (i = 0; i < 0x14; i ++) + write_attr_registers(i, mode->attr_regs[i]); + for (i = 0; i < 0x09; i ++) + write_graph_registers(i, mode->graph_regs[i]); +} + +void voodoo_set_desktop_regs(uint32 bits_per_pixel, display_mode *dm) +{ + uint32 miscinit0=0; + int vidpll, fout; + int vidproc = VIDPROCDEFAULT; + uint32 bpp = (bits_per_pixel + 7) / 8; + uint32 bytes_per_row = (dm->virtual_width * bits_per_pixel + 7) / 8; + + accel.bpp = bits_per_pixel; + accel.width = dm->virtual_width; + accel.height = dm->virtual_height; + voodoo3_setup_monitor(dm); + + vidproc &= ~(0x1c0000); // clear bits 18 to 20, bpp in vidproccfg + vidproc |= ((bpp - 1) << VIDCFG_PIXFMT_SHIFT); // enable bits 18 to 20 to the required bpp + vidpll = voodoo3_calc_pll(dm->timing.pixel_clock, &fout, 0); + + switch(bits_per_pixel) { // bit 10 of vidproccfg, is enabled or disabled as needed for the Desktop + case 8: + vidproc &= ~(1 << 10); // bit 10 off for palettized modes only, off means palette is used + break; + case 16: + #if __POWERPC__ + miscinit0 = 0xc0000000; + #endif + vidproc |= (1 << 10); // bypass palette for 16bit modes + break; + case 32: + #if __POWERPC__ + miscinit0 = 0x40000000; + #endif + vidproc |= (1 << 10); // Same for 32bit modes + break; + } + + voodoo3_wait_idle(); + + voodoo3_write32(MISCINIT1, voodoo3_read32(MISCINIT1) | 0x01); + + voodoo3_make_room(4); + voodoo3_write32(VGAINIT0, 4928); + voodoo3_write32(DACMODE, 0); + voodoo3_write32(VIDDESKSTRIDE, bytes_per_row); + voodoo3_write32(PLLCTRL0, vidpll); + + voodoo3_make_room(5); + voodoo3_write32(VIDSCREENSIZE, dm->virtual_width | (dm->virtual_height << 12)); + voodoo3_write32(VIDDESKSTART, 1024); + voodoo3_write32(VIDPROCCFG, vidproc); + voodoo3_write32(VGAINIT1, 0); + voodoo3_write32(MISCINIT0, miscinit0); + + voodoo3_make_room(8); + voodoo3_write32(SRCBASE, 1024); + voodoo3_write32(DSTBASE, 1024); + voodoo3_write32(COMMANDEXTRA_2D, 0); + voodoo3_write32(CLIP0MIN, 0); + voodoo3_write32(CLIP0MAX, 0x0fff0fff); + voodoo3_write32(CLIP1MIN, 0); + voodoo3_write32(CLIP1MAX, 0x0fff0fff); + voodoo3_write32(SRCXY, 0); + voodoo3_wait_idle(); +} + +#if __POWERPC__ +static uint32 ppcSwap(uint32 val, uint32 bpp) { + switch (bpp) { + default: + return val; + case 16: + return ((val & 0x00ff00ff) << 8) | + ((val & 0xff00ff00) >> 8); + case 24: + case 32: + return ((((val) & 0x000000ff) << 24) | + (((val) & 0x0000ff00) << 8) | + (((val) & 0x00ff0000) >> 8) | + (((val) & 0xff000000) >> 24)); + } +} +#endif + + +void voodoo3_set_cursor(uint8 *data, uint16 width, uint8 height, uint8 *andMask, uint8 *xorMask) +{ + int i; + uint32 fbcurptr=voodoo3_read32(HWCURPATADDR); + uint8 *cursor = (uint8 *)data + fbcurptr; + uint16 *xordata = (uint16 *)xorMask; + uint16 *anddata = (uint16 *)andMask; + uint64 *cursor64 = (uint64 *)cursor; +#if __POWERPC__ + uint32 *cursor32 = (uint32 *)data + fbcurptr; +#endif + for(i = 0; i < 16; i++) { + *cursor64++ = ((~0 << 16) | (*anddata++)); + *cursor64++ = ((0 << 16) | (*xordata++)); + } + + for(i = 128; i < 176; i++) { + *cursor64++ = ~0; + *cursor64++ = 0; + } +// for PPC Byteswapping +#if __POWERPC__ + for (i=0; i<256; i++) { + uint32 val = cursor32[i]; + cursor32[i] = ppcSwap(val,accel.bpp); + } +#endif + +} + +void voodoo3_init_cursor_address() +{ + + voodoo3_make_room(1); + voodoo3_write32(HWCURPATADDR, 0); + voodoo3_wait_idle(); + +} + +void voodoo3_set_cursor_colors(int fg, int bg) +{ + + + voodoo3_make_room(2); + voodoo3_write32(HWCURC0, bg & 0xffffff); + voodoo3_write32(HWCURC1, fg & 0xffffff); + voodoo3_wait_idle(); + +} + +void voodoo3_show_cursor() +{ + + voodoo3_make_room(1); + voodoo3_write32(VIDPROCCFG, voodoo3_read32(VIDPROCCFG) | (1 << 27)); + voodoo3_wait_idle(); + +} + +void voodoo3_hide_cursor() +{ + + voodoo3_make_room(1); + voodoo3_write32(VIDPROCCFG, voodoo3_read32(VIDPROCCFG) & ~(1 << 27)); + voodoo3_wait_idle(); + +} + +void voodoo3_move_cursor(int x, int y) +{ + #if __INTEL__ + #define xoffset 63 + #define yoffset 63 + #else + #define xoffset 15 + #define yoffset 63 + #endif + x += xoffset; + y += yoffset; + voodoo3_write32(HWCURLOC, x | (y << 16)); +} + +void voodoo3_screen_to_screen_blit(list_packet_blit *list, uint32 bytes_per_row, uint32 bits_per_pixel) +{ + uint32 fmt = bytes_per_row | ((bits_per_pixel + ((bits_per_pixel == 8) ? 0 : 8)) << 13); + uint32 blitcmd = COMMAND_2D_S2S_BITBLT | (ROP_COPY << 24); + + if (list->src_x <= list->dest_x) { + blitcmd |= BIT(14); + list->src_x += (list->width - 1); list->dest_x += (list->width - 1); + } + if (list->src_y <= list->dest_y) { + blitcmd |= BIT(15); + list->src_y += (list->height - 1); list->dest_y += (list->height - 1); + } + voodoo3_make_room(6); + + voodoo3_write32(SRCFORMAT, fmt); + voodoo3_write32(DSTFORMAT, fmt); + voodoo3_write32(COMMAND_2D, blitcmd); + voodoo3_write32(DSTSIZE, list->width | (list->height << 16)); + voodoo3_write32(DSTXY, list->dest_x | (list->dest_y << 16)); + voodoo3_write32(LAUNCH_2D, list->src_x | (list->src_y << 16)); + voodoo3_wait_idle(); + +} + +void voodoo3_fill_rect(list_packet *list, uint32 color, uint32 bytes_per_row, uint32 bits_per_pixel) +{ + uint32 fmt; + fmt = bytes_per_row | ((bits_per_pixel + ((bits_per_pixel == 8) ? 0 : 8)) << 13); + + voodoo3_make_room(5); + voodoo3_write32(DSTFORMAT, fmt); + voodoo3_write32(COLORFORE, color); + voodoo3_write32(COMMAND_2D, COMMAND_2D_FILLRECT | (ROP_COPY << 24)); + voodoo3_write32(DSTSIZE, list->w | (list->h << 16)); + voodoo3_write32(LAUNCH_2D, list->x | (list->y << 16)); + voodoo3_wait_idle(); + +} + +void voodoo3_invert_rect(list_packet *list, uint32 color, uint32 bytes_per_row, uint32 bits_per_pixel) +{ + uint32 fmt; + fmt = bytes_per_row | ((bits_per_pixel + ((bits_per_pixel == 8) ? 0 : 8)) << 13); + + voodoo3_make_room(5); + voodoo3_write32(DSTFORMAT, fmt); + voodoo3_write32(COLORFORE, color); + voodoo3_write32(COMMAND_2D, COMMAND_2D_FILLRECT | (ROP_INVERT << 24)); + voodoo3_write32(DSTSIZE, list->w | (list->h << 16)); + voodoo3_write32(LAUNCH_2D, list->x | (list->y << 16)); + voodoo3_wait_idle(); + +} + +void voodoo3_fill_span(list_packet *list, uint32 color, uint32 bytes_per_row, uint32 bits_per_pixel) +{ + uint32 fmt; + fmt = bytes_per_row | ((bits_per_pixel + ((bits_per_pixel == 8) ? 0 : 8)) << 13); + + voodoo3_make_room(5); + voodoo3_write32(DSTFORMAT, fmt); + voodoo3_write32(COLORFORE, color); + voodoo3_write32(COMMAND_2D, COMMAND_2D_FILLRECT | (ROP_COPY << 24)); + voodoo3_write32(DSTSIZE, list->w | (list->h << 16)); + voodoo3_write32(LAUNCH_2D, list->x | (list->y << 16)); + voodoo3_wait_idle(); + +} + +void voodoo3_set_palette(int index, uint32 color) +{ + + voodoo3_make_room(2); + voodoo3_write32(DACADDR, index); + voodoo3_write32(DACDATA, color); + +} + +uint32 voodoo3_get_memory_size() { + uint32 draminit0 = 0; + uint32 draminit1 = 0; + uint32 miscinit1 = 0; + uint32 lfbsize = 0; + int32 sgram_p = 0; + draminit0 = voodoo3_read32(DRAMINIT0); + draminit1 = voodoo3_read32(DRAMINIT1); + sgram_p = (draminit1 & DRAMINIT1_MEM_SDRAM) ? 0 : 1; + lfbsize = sgram_p ? + (((draminit0 & DRAMINIT0_SGRAM_NUM) ? 2 : 1) * + ((draminit0 & DRAMINIT0_SGRAM_TYPE) ? 8 : 4) * 1024 * 1024) : + 16 * 1024 * 1024; + + /* disable block writes for SDRAM (why?) */ + miscinit1 = voodoo3_read32(MISCINIT1); + miscinit1 |= sgram_p ? 0 : MISCINIT1_2DBLOCK_DIS; + miscinit1 |= MISCINIT1_CLUT_INV; + voodoo3_make_room(1); + voodoo3_write32(MISCINIT1, miscinit1); + return lfbsize; +} + +void voodoo3_display_overlay(const overlay_window *ow, const overlay_buffer *ob, const overlay_view* ov) { + + uint8 red, green, blue; + + int32 offset; + int32 pitch; + int32 dudx, dvdy; + uint32 left=0; + uint32 srcwidth; + uint32 vidcfg, stride; + uint32 x1, y1, x2, y2; + uint32 offset_left, offset_top; + + if (ow==NULL||ob==NULL||ov==NULL) return; + + offset=(uint32)ob->buffer_dma; // Start OFFSET as PCI Space + pitch=((ob->width<<1) + 3) & ~3; // offset (of Desktop && Overlay) + + + offset_left=ow->h_start<0?-ow->h_start:0; + offset_top=ow->v_start<0?-ow->v_start:0; + + dudx = (ob->width << 20) / (ow->width); + dvdy = (ob->height << 20) / (ow->height); + + left = ((offset_left*dudx<<16) & 0x0001ffff) << 3; + + x1=ow->h_start + offset_left; + y1=ow->v_start + offset_top; + x2=x1 + ow->width; + if (x2>=accel.width) x2=accel.width; + y2=y1 + ow->height; + if (y2>=accel.height) y2=accel.height; + + offset+=((offset_left*dudx>>20) & ~1) << 1; + srcwidth=(ob->width<<20); + + vidcfg=voodoo3_read32(VIDPROCCFG); + stride=voodoo3_read32(VIDDESKTOPOVERLAYSTRIDE); + + vidcfg &= ~VIDPROCCFGMASK; + vidcfg |= VIDCFG_CHROMA_KEY | VIDCFG_OVL_ENABLE | VIDCFG_OVL_NOT_VIDEO_IN | + VIDCFG_OVL_CLUT_BYPASS; + + if(ob->width != ow->width) + vidcfg |= VIDCFG_OVL_HSCALE; + if(ob->height != ow->height) + vidcfg |= VIDCFG_OVL_VSCALE; + + switch( ob->space) { + case B_YCbCr422: +#if __POWERPC__ + switch(accel.bpp) { + case 8: + vidcfg |= VIDCFG_OVL_FMT_YUYV422 << VIDCFG_OVL_FMT_SHIFT; + break; + case 16: + vidcfg |= VIDCFG_OVL_FMT_UYVY422 << VIDCFG_OVL_FMT_SHIFT; + break; + default: + ddpf(( "YCbCr overlays aren't supported in 32 bit mode" )); + return; + } +#else + vidcfg |= VIDCFG_OVL_FMT_YUYV422 << VIDCFG_OVL_FMT_SHIFT; +#endif + break; +#if __POWERPC__ + case B_RGB16_BIG: +#else + case B_RGB16: +#endif + vidcfg |= VIDCFG_OVL_FMT_RGB565 << VIDCFG_OVL_FMT_SHIFT; + break; + default: return; + + } + + /* can't do bilinear filtering when in 2X mode */ + if( (vidcfg & VIDCFG_2X) == 0 ) + vidcfg |= VIDCFG_OVL_FILTER_BILIN << VIDCFG_OVL_FILTER_SHIFT; + + voodoo3_make_room( 12 ); + + voodoo3_write32(VIDPROCCFG, vidcfg); + + if ((ow->flags & B_OVERLAY_COLOR_KEY)==B_OVERLAY_COLOR_KEY) { + uint32 color=0; + red=ow->red.value & ow->red.mask; + green=ow->green.value & ow->green.mask; + blue=ow->blue.value & ow->blue.mask; + switch (voodoo3_bits_per_pixel()) { + case(8): + case(32): + color= (red << 16)+(green << 8)+(blue << 0); + break; + case(16): + color= (red << 11)+(green << 5)+(blue << 0); + break; + } + voodoo3_write32(VIDCHROMAMIN, color); + voodoo3_write32(VIDCHROMAMAX, color); + } + + /* Setting up position of Overlaywindow on Screen */ + voodoo3_write32(VIDOVERLAYSTARTCOORDS, x1 | (y1 << 12)); + voodoo3_write32(VIDOVERLAYENDSCREENCOORDS, (x2 - 1) | ((y2 - 1) << 12)); + /* Setting up scale and position of overlay in Graphics Memory */ + voodoo3_write32(VIDOVERLAYDUDX, dudx); + voodoo3_write32(VIDOVERLAYDUDXOFFSETSRCWIDTH, left | srcwidth); + voodoo3_write32(VIDOVERLAYDVDY, dvdy); + voodoo3_write32(VIDOVERLAYDVDYOFFSET, (y1 & 0x0000ffff) << 3); + + stride &= 0x0000ffff; + stride |= pitch << 16; + + voodoo3_write32(VIDDESKTOPOVERLAYSTRIDE, stride); + voodoo3_write32(SST_3D_LEFTOVERLAYBUF, offset); + voodoo3_write32(VIDINADDR0, offset); +} + +void voodoo3_reset_overlay() { + + uint32 vidcfg, stride; + + vidcfg=voodoo3_read32(VIDPROCCFG); + stride=voodoo3_read32(VIDDESKTOPOVERLAYSTRIDE); + + + /* reset the video */ + vidcfg &= ~VIDPROCCFGMASK; + voodoo3_write32(VIDPROCCFG, vidcfg); + voodoo3_write32(RGBMAXDELTA, 0x0080808); + voodoo3_write32(VIDCHROMAMIN, 0); + voodoo3_write32(VIDCHROMAMAX, 0); + +} + +// To stop the overlay +void voodoo3_stop_overlay() { + + uint32 vidcfg; + + vidcfg=voodoo3_read32(VIDPROCCFG); + + /* reset the video */ + vidcfg &= ~VIDPROCCFGMASK; + voodoo3_write32(VIDPROCCFG, vidcfg); + +} + diff --git a/src/add-ons/accelerants/tdfx/voodoo3_accelerant.h b/src/add-ons/accelerants/tdfx/voodoo3_accelerant.h new file mode 100644 index 0000000000..4b4930b9d7 --- /dev/null +++ b/src/add-ons/accelerants/tdfx/voodoo3_accelerant.h @@ -0,0 +1,82 @@ +/* + - Overlay support + - PPC Port: Andreas Drewke (andreas_dr@gmx.de) + - Voodoo3Driver 0.02 (c) by Carwyn Jones (2002) + +*/ + +#ifndef __VOODOO3_ACCELERANT_H__ +#define __VOODOO3_ACCELERANT_H__ + +#include +#include + +typedef struct +{ + uint8 *regs_base; + uint32 io_base; + uint32 bpp, width, height; +} Voodoo3Accel; + +typedef struct +{ + uint8 seq_regs[5]; + uint8 crtc_regs[25]; + uint8 attr_regs[21]; + uint8 graph_regs[9]; + uint8 misc; +} MonitorRegs; + +typedef struct +{ + int x; + int y; + int w; + int h; +} list_packet; + +typedef struct +{ + int src_x; + int src_y; + int dest_x; + int dest_y; + int width; + int height; +} list_packet_blit; + +// Initialize +int voodoo3_init(uint8 *registers_base, uint32 io_base); +void voodoo3_set_monitor_defaults(); +void voodoo3_wait_idle(); +void voodoo_set_desktop_regs(uint32 bpp, display_mode *dm); + +// Cursor functionality +void voodoo3_show_cursor(); +void voodoo3_hide_cursor(); +void voodoo3_move_cursor(int x, int y); +void voodoo3_set_cursor(uint8 *data, uint16 width, uint8 height, uint8 *andMask, uint8 *xorMask); +void voodoo3_set_cursor_colors(int bg, int fg); +void voodoo3_init_cursor_address(); + +// handle colortable +void voodoo3_set_palette(int index, uint32 color); + +// accelerated 2d functions +void voodoo3_screen_to_screen_blit(list_packet_blit *list, uint32 bytes_per_row, uint32 bits_per_pixel); +void voodoo3_fill_rect(list_packet *list, uint32 color, uint32 bytes_per_row, uint32 bits_per_pixel); +void voodoo3_invert_rect(list_packet *list, uint32 color, uint32 bytes_per_row, uint32 bits_per_pixel); +void voodoo3_fill_span(list_packet *list, uint32 color, uint32 bytes_per_row, uint32 bits_per_pixel); + +// Runtime information +uint32 voodoo3_bits_per_pixel(); +uint32 voodoo3_get_memory_size(); + +void voodoo3_setup_monitor(display_mode *); + +// Overlay +void voodoo3_display_overlay(const overlay_window *ow, const overlay_buffer *ob, const overlay_view* ov); +void voodoo3_reset_overlay(); +void voodoo3_stop_overlay(); + +#endif diff --git a/src/add-ons/accelerants/tdfx/voodoo3_io.h b/src/add-ons/accelerants/tdfx/voodoo3_io.h new file mode 100644 index 0000000000..42e954af6f --- /dev/null +++ b/src/add-ons/accelerants/tdfx/voodoo3_io.h @@ -0,0 +1,182 @@ +#ifndef __VOODOO3_IO_H__ +#define __VOODOO3_IO_H__ + +#include + +// voodoo3 register locations in pci memory space +/* membase0 register offsets */ +#define STATUS 0x00 +#define PCIINIT0 0x04 +#define SIPMONITOR 0x08 +#define LFBMEMORYCONFIG 0x0c +#define MISCINIT0 0x10 +#define MISCINIT1 0x14 +#define DRAMINIT0 0x18 +#define DRAMINIT1 0x1c +#define AGPINIT 0x20 +#define TMUGBEINIT 0x24 +#define VGAINIT0 0x28 +#define VGAINIT1 0x2c +#define DRAMCOMMAND 0x30 +#define DRAMDATA 0x34 +/* reserved 0x38 */ +/* reserved 0x3c */ +#define PLLCTRL0 0x40 +#define PLLCTRL1 0x44 +#define PLLCTRL2 0x48 +#define DACMODE 0x4c +#define DACADDR 0x50 +#define DACDATA 0x54 +#define RGBMAXDELTA 0x58 +#define VIDPROCCFG 0x5c +#define HWCURPATADDR 0x60 +#define HWCURLOC 0x64 +#define HWCURC0 0x68 +#define HWCURC1 0x6c +#define VIDINFORMAT 0x70 +#define VIDINSTATUS 0x74 +#define VIDSERPARPORT 0x78 +#define VIDINXDELTA 0x7c +#define VIDININITERR 0x80 +#define VIDINYDELTA 0x84 +#define VIDPIXBUFTHOLD 0x88 +#define VIDCHRMIN 0x8c +#define VIDCHRMAX 0x90 +#define VIDCURLIN 0x94 +#define VIDSCREENSIZE 0x98 +#define VIDOVRSTARTCRD 0x9c +#define VIDOVRENDCRD 0xa0 +#define VIDOVRDUDX 0xa4 +#define VIDOVRDUDXOFF 0xa8 +#define VIDOVRDVDY 0xac +/* ... */ + +#define VIDOVRDVDYOFF 0xe0 +#define VIDDESKSTART 0xe4 +#define VIDDESKSTRIDE 0xe8 +#define VIDINADDR0 0xec +#define VIDINADDR1 0xf0 +#define VIDINADDR2 0xf4 +#define VIDINSTRIDE 0xf8 +#define VIDCUROVRSTART 0xfc +#define VIDOVERLAYSTARTCOORDS 0x9c +#define VIDOVERLAYENDSCREENCOORDS 0xa0 +#define VIDOVERLAYDUDX 0xa4 +#define VIDOVERLAYDUDXOFFSETSRCWIDTH 0xa8 +#define VIDOVERLAYDVDY 0xac +#define VIDOVERLAYDVDYOFFSET 0xe0 + +#define SST_3D_OFFSET 0x200000 +#define SST_3D_LEFTOVERLAYBUF SST_3D_OFFSET+0x250 + +#define INTCTRL (0x00100000 + 0x04) +#define CLIP0MIN (0x00100000 + 0x08) +#define CLIP0MAX (0x00100000 + 0x0c) +#define DSTBASE (0x00100000 + 0x10) +#define DSTFORMAT (0x00100000 + 0x14) +#define SRCBASE (0x00100000 + 0x34) +#define COMMANDEXTRA_2D (0x00100000 + 0x38) +#define CLIP1MIN (0x00100000 + 0x4c) +#define CLIP1MAX (0x00100000 + 0x50) +#define SRCFORMAT (0x00100000 + 0x54) +#define SRCSIZE (0x00100000 + 0x58) +#define SRCXY (0x00100000 + 0x5c) +#define COLORBACK (0x00100000 + 0x60) +#define COLORFORE (0x00100000 + 0x64) +#define DSTSIZE (0x00100000 + 0x68) +#define DSTXY (0x00100000 + 0x6c) +#define COMMAND_2D (0x00100000 + 0x70) +#define LAUNCH_2D (0x00100000 + 0x80) + +#define COMMAND_3D (0x00200000 + 0x120) + +/* register bitfields (not all, only as needed) */ + +#define BIT(x) (1UL << (x)) + +/* COMMAND_2D reg. values */ +#define ROP_COPY 0xcc // src +#define ROP_INVERT 0x55 // NOT dst +#define ROP_XOR 0x66 // src XOR dst + +#define AUTOINC_DSTX BIT(10) +#define AUTOINC_DSTY BIT(11) +#define COMMAND_2D_FILLRECT 0x05 +#define COMMAND_2D_S2S_BITBLT 0x01 // screen to screen +#define COMMAND_2D_H2S_BITBLT 0x03 // host to screen + + +#define COMMAND_3D_NOP 0x00 +#define STATUS_RETRACE BIT(6) +#define STATUS_BUSY BIT(9) +#define MISCINIT1_CLUT_INV BIT(0) +#define MISCINIT1_2DBLOCK_DIS BIT(15) +#define DRAMINIT0_SGRAM_NUM BIT(26) +#define DRAMINIT0_SGRAM_TYPE BIT(27) +#define DRAMINIT1_MEM_SDRAM BIT(30) +#define VGAINIT0_VGA_DISABLE BIT(0) +#define VGAINIT0_EXT_TIMING BIT(1) +#define VGAINIT0_8BIT_DAC BIT(2) +#define VGAINIT0_EXT_ENABLE BIT(6) +#define VGAINIT0_WAKEUP_3C3 BIT(8) +#define VGAINIT0_LEGACY_DISABLE BIT(9) +#define VGAINIT0_ALT_READBACK BIT(10) +#define VGAINIT0_FAST_BLINK BIT(11) +#define VGAINIT0_EXTSHIFTOUT BIT(12) +#define VGAINIT0_DECODE_3C6 BIT(13) +#define VGAINIT0_SGRAM_HBLANK_DISABLE BIT(22) +#define VGAINIT1_MASK 0x1fffff +#define VIDCFG_VIDPROC_ENABLE BIT(0) +#define VIDCFG_CURS_X11 BIT(1) +#define VIDCFG_HALF_MODE BIT(4) +#define VIDCFG_CHROMA_KEY BIT(5) +#define VIDCFG_CHROMA_KEY_INVERSION BIT(6) +#define VIDCFG_DESK_ENABLE BIT(7) +#define VIDCFG_OVL_ENABLE BIT(8) +#define VIDCFG_OVL_NOT_VIDEO_IN BIT(9) +#define VIDCFG_CLUT_BYPASS BIT(10) +#define VIDCFG_OVL_CLUT_BYPASS BIT(11) +#define VIDCFG_OVL_HSCALE BIT(14) +#define VIDCFG_OVL_VSCALE BIT(15) +#define VIDCFG_OVL_FILTER_SHIFT 16 +#define VIDCFG_OVL_FILTER_POINT 0 +#define VIDCFG_OVL_FILTER_2X2 1 +#define VIDCFG_OVL_FILTER_4X4 2 +#define VIDCFG_OVL_FILTER_BILIN 3 +#define VIDCFG_OVL_FMT_SHIFT 21 +#define VIDCFG_OVL_FMT_RGB565 1 +#define VIDCFG_OVL_FMT_YUV411 4 +#define VIDCFG_OVL_FMT_YUYV422 5 +#define VIDCFG_OVL_FMT_UYVY422 6 +#define VIDCFG_OVL_FMT_RGB565_DITHER 7 + +#define VIDCFG_2X BIT(26) +#define VIDCFG_HWCURSOR_ENABLE BIT(27) +#define VIDCFG_PIXFMT_SHIFT 18 +#define DACMODE_2X BIT(0) +#define VIDPROCCFGMASK 0xa2e3eb6c +#define VIDPROCDEFAULT 134481025 + +#define VIDCHROMAMIN 0x8c +#define VIDCHROMAMAX 0x90 +#define VIDDESKTOPOVERLAYSTRIDE 0xe8 + +#define CRTC_INDEX 0x3d4 +#define CRTC_DATA 0x3d5 +#define SEQ_INDEX 0x3c4 +#define SEQ_DATA 0x3c5 +#define MISC_W 0x3c2 +#define GRA_INDEX 0x3ce +#define ATT_IW 0x3c0 +#define IS1_R 0x3da + +// pci read from memory +#define PCI_MEM_RD_8(address) (*(volatile uint8 *) (address)) +#define PCI_MEM_RD_16(address) B_LENDIAN_TO_HOST_INT16((*(volatile uint16 *) (address))) +#define PCI_MEM_RD_32(address) B_LENDIAN_TO_HOST_INT32((*(volatile uint32 *) (address))) +// pci write to memory +#define PCI_MEM_WR_8(address, value) ((*(volatile uint8 *) (address)) = (uint8) (value)) +#define PCI_MEM_WR_16(address, value) ((*(volatile uint16 *) (address)) = (uint16) B_HOST_TO_LENDIAN_INT16((value)) +#define PCI_MEM_WR_32(address, value) ((*(volatile uint32 *) (address)) = (uint32) B_HOST_TO_LENDIAN_INT32(value)) + +#endif diff --git a/src/add-ons/kernel/drivers/graphics/Jamfile b/src/add-ons/kernel/drivers/graphics/Jamfile index be6dc89c91..1dce7a2e1a 100644 --- a/src/add-ons/kernel/drivers/graphics/Jamfile +++ b/src/add-ons/kernel/drivers/graphics/Jamfile @@ -4,3 +4,4 @@ SubInclude OBOS_TOP src add-ons kernel drivers graphics matrox ; SubInclude OBOS_TOP src add-ons kernel drivers graphics neomagic ; SubInclude OBOS_TOP src add-ons kernel drivers graphics nvidia ; SubInclude OBOS_TOP src add-ons kernel drivers graphics radeon ; +SubInclude OBOS_TOP src add-ons kernel drivers graphics tdfx ; diff --git a/src/add-ons/kernel/drivers/graphics/tdfx/Jamfile b/src/add-ons/kernel/drivers/graphics/tdfx/Jamfile new file mode 100644 index 0000000000..65cbd2c734 --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/tdfx/Jamfile @@ -0,0 +1,17 @@ +SubDir OBOS_TOP src add-ons kernel drivers graphics tdfx ; + +UsePrivateHeaders graphics ; +UsePrivateHeaders [ FDirName graphics tdfx ] ; + +R5KernelAddon tdfx.driver : kernel drivers bin : + driver.c +; + +# Link to kernel/drivers/dev/graphics +{ + local dir = [ FDirName $(OBOS_ADDON_DIR) kernel drivers dev graphics ] ; + local instDriver = tdfx.driver ; + MakeLocate $(instDriver) : $(dir) ; + RelSymLink $(instDriver) : tdfx.driver ; +} + diff --git a/src/add-ons/kernel/drivers/graphics/tdfx/driver.c b/src/add-ons/kernel/drivers/graphics/tdfx/driver.c new file mode 100644 index 0000000000..5608541880 --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/tdfx/driver.c @@ -0,0 +1,1096 @@ +/* + Copyright 1999, Be Incorporated. All Rights Reserved. + This file may be used under the terms of the Be Sample Code License. + + - PPC Port: Andreas Drewke (andreas_dr@gmx.de) + - Voodoo3Driver 0.02 (c) by Carwyn Jones (2002) + +*/ + +/* standard kernel driver stuff */ +#include +#include +#include +#include +#include + +/* this is for the standardized portion of the driver API */ +/* currently only one operation is defined: B_GET_ACCELERANT_SIGNATURE */ +#include + +/* this is for sprintf() */ +#include + +/* this is for string compares */ +#include + +/* The private interface between the accelerant and the kernel driver. */ +#include +#include + +#define DEBUG 1 +#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 voodoo_interrupt(void *data); + +#if DEBUG > 0 +static int voodoodump(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 0x121A /* 3dfx vendor */ + +#define BANSHEE 0x3 +#define VOODOO3 0x5 + +static uint16 voodoo_device_list[] = +{ + BANSHEE, // Voodoo Banshee + VOODOO3, // Voodoo + 0 +}; + +static struct +{ + uint16 vendor; + uint16 *devices; +} SupportedDevices[] = +{ + {VENDOR_ID, voodoo_device_list}, + {0x0000, NULL} +}; + +// IO Access Macros for accessing IOBase + +#if __INTEL__ + +#define outportb(offset, value) (*pci_bus->write_io_8)((offset), (value)) +#define outportw(offset, value) (*pci_bus->write_io_16)((offset), (value)) +#define outportl(offset, value) (*pci_bus->write_io_32)((offset), (value)) +#define inportb(offset) ((*pci_bus->read_io_8)(offset)) +#define inportw(offset) ((*pci_bus->read_io_16)(offset)) +#define inportl(offset) ((*pci_bus->read_io_32)(offset)) + +#elif __POWERPC__ + +#define inportb(offset) (*((volatile uint8*)(offset))) //__eieio() +#define outportb(offset, value) (*((volatile uint8*)(offset)) = (value)); //__eieio() +#define inportw(offset) B_LENDIAN_TO_HOST_INT16((*((volatile uint16*)(offset)))) //__eieio() +#define outportw(offset, value) (*((volatile uint16*)(offset)) = B_HOST_TO_LENDIAN_INT16((value))); //__eieio() +#define inportl(offset) B_LENDIAN_TO_HOST_INT32((*((volatile uint32*)(offset)))) //__eieio() +#define outportl(offset, value) (*((volatile uint32*)(offset)) = B_HOST_TO_LENDIAN_INT32((value))); //__eieio() + +#endif + + +/* TODO */ +#define voodoo3_disableirq() +#define voodoo3_enableirq() +#define voodoo3_clearirq() +#define voodoo3_irqpending() 0 + + +/* + 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(("TDFXV3: 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(("TDFXV3: we support this device\n")); + found_one = TRUE; + goto done; + } + /* next supported device */ + devices++; + } + } + vendor++; + } + /* next pci_info struct, please */ + pci_index++; + } + ddprintf(("TDFXV3: 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(("TDFXV3: 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("voodoodump", voodoodump, "dump VOODOO kernel driver persistant data"); +#endif + ddprintf(("TDFXV3: 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("voodoodump", voodoodump); +#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) +{ + char buffer[B_OS_NAME_LENGTH]; + shared_info *si = di->si; + uint16 tmp; + pci_info *pcii = &(di->pcii); +#if __POWERPC__ + int32 offset=0; +#endif + int32 _regs = di->pcii.u.h0.base_registers[0]; + int32 iobase = di->pcii.u.h0.base_registers[2]; + int32 fb = di->pcii.u.h0.base_registers[1]; + +#if __POWERPC__ + /* On PowerPC we would get INSANE Values, so we assume them */ + int32 _regs_size = 1 << 25; + int32 iobase_size = di->pcii.u.h0.base_register_sizes[2]; + int32 fb_size = 1 << 24; +#elif __INTEL__ + int32 _regs_size = di->pcii.u.h0.base_register_sizes[0]; + int32 iobase_size = di->pcii.u.h0.base_register_sizes[2]; + int32 fb_size = di->pcii.u.h0.base_register_sizes[1]; +#endif + + ddprintf(("TDFXV3: enter map_device\n")); + /* enable memory mapped IO, disable VGA I/O */ + tmp = get_pci(PCI_command, 2); + tmp |= (PCI_command_memory | PCI_command_io); + set_pci(PCI_command, 2, tmp); + + ddprintf (("TDFXV3: regs: 0x%lx, size:0x%lx\n",_regs, _regs_size)); + ddprintf (("TDFXV3: io: 0x%lx, size:0x%lx\n",iobase, iobase_size)); + ddprintf (("TDFXV3: fb: 0x%lx, size:0x%lx\n",fb, fb_size)); + + /* 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, + _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; + +#if __INTEL__ + iobase_size = (iobase_size+(B_PAGE_SIZE-1))&~(B_PAGE_SIZE-1); + + sprintf(buffer, "%04X_%04X_%02X%02X%02X io", + di->pcii.vendor_id, di->pcii.device_id, + di->pcii.bus, di->pcii.device, di->pcii.function); + si->io_area = map_physical_memory( + buffer, + (void*)iobase, + iobase_size, + B_ANY_KERNEL_ADDRESS, + B_READ_AREA | B_WRITE_AREA, + (void **)&(si->io)); + // return the error if there was some problem + + /* remember the io_base address for inb, outb */ + si->iobase = iobase; + + +#else +// PPC comes here + + /* Round down to nearest page boundary */ + iobase = iobase & ~(B_PAGE_SIZE-1); + /* Adjust the size */ + offset = di->pcii.u.h0.base_registers[2] - iobase; + iobase_size += offset; + iobase_size=(iobase_size +(B_PAGE_SIZE-1)) & ~(B_PAGE_SIZE-1); + + sprintf(buffer, "%04X_%04X_%02X%02X%02X io", + di->pcii.vendor_id, di->pcii.device_id, + di->pcii.bus, di->pcii.device, di->pcii.function); + si->io_area = map_physical_memory( + buffer, + (void*)iobase, + iobase_size, + B_ANY_KERNEL_ADDRESS, + B_READ_AREA | B_WRITE_AREA, + (void **)&si->io); + // return the error if there was some problem + + /* remember the io_base address for inb, outb */ + si->iobase = si->io + offset; + + ddprintf (("TDFXV3: ioppc_3_cor: 0x%x, size:%li\n",iobase+offset, iobase_size)); + ddprintf (("TDFXV3: ioppc_4(vM): 0x%x, size:%li\n",si->iobase, iobase_size)); + +#endif + if (si->io_area < 0) + { + ddprintf(("TDFXV3: could not map iobase")); + delete_area(si->regs_area); + si->regs_area = -1; + return si->io_area; + } + sprintf(buffer, "%04X_%04X_%02X%02X%02X framebuffer", + di->pcii.vendor_id, di->pcii.device_id, + di->pcii.bus, di->pcii.device, di->pcii.function); + si->fb_area = map_physical_memory( + buffer, + (void *)fb, + 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, + 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->io_area); + si->io_area = -1; + } + /* remember the DMA address of the frame buffer for BDirectWindow purposes */ + si->framebuffer_pci = (void *)fb; + + /* in any case, return the result */ + ddprintf(("TDFXV3: leave map_device\n")); + return si->fb_area; +} + +static void unmap_device(device_info *di) +{ + shared_info *si = di->si; + uint32 tmp; + pci_info *pcii = &(di->pcii); + + ddprintf(("TDFXV3: 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 */ + tmp = get_pci(PCI_command, 4); + tmp &= 0xfffffffc; + set_pci(PCI_command, 4, tmp); + /* delete the areas */ + if (si->io_area >= 0) delete_area(si->io_area); + if (si->regs_area >= 0) delete_area(si->regs_area); + if (si->fb_area >= 0) delete_area(si->fb_area); + si->io_area = si->regs_area = si->fb_area = -1; + si->framebuffer = NULL; + di->regs = NULL; + si->io = NULL; + ddprintf(("TDFXV3: unmap_device() ends.\n")); +} + +static void probe_devices(void) +{ + uint32 pci_index = 0; + uint32 count = 0; + device_info *di = pd->di; + + ddprintf(("TDFXV3: 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(("TDFXV3: 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(("TDFXV3: 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(("TDFXV3: 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 +voodoo_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 (voodoo3_irqpending()==1) + { + /* do our stuff */ + handled = thread_interrupt_work(flags, regs, si); +#if DEBUG > 0 + /* increment the counter for this device */ + di->interrupt_count++; +#endif + voodoo3_clearirq(); + } + + /* 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 suppoesed to handle interrupts still? */ + if (atomic_and(flags, -1) & SKD_HANDLER_INSTALLED) { + /* reschedule with same period by default */ + bigtime_t when = si->refresh_period; + timer *to; + + /* if interrupts are "enabled", do our thing */ + if (di->can_interrupt) { + /* insert code to sync to interrupts here */ + if (!vbl_status) { + when -= si->blank_period - 4; + } + /* do the things we do when we notice a vertical retrace */ + result = thread_interrupt_work(flags, regs, si); + + } + + /* pick the "other" timer */ + to = (timer *)&(di->ti_a); + if (to == te) to = (timer *)&(di->ti_b); + /* our guess as to when we should be back */ + ((timer_info *)to)->when_target = now + when; + /* reschedule the interrupt */ + add_timer(to, timer_interrupt_func, ((timer_info *)to)->when_target, B_ONE_SHOT_ABSOLUTE_TIMER); + /* remember the currently active timer */ + di->current_timer = (timer_info *)to; + } + + return result; +} +#else +static int32 fake_interrupt_thread_func(void *_di) +{ + device_info *di = (device_info *)_di; + shared_info *si = di->si; + int32 *flags = &(si->flags); + vuint32 *regs = di->regs; + + bigtime_t last_sync; + bigtime_t this_sync; + bigtime_t diff_sync; + + uint32 counter = 1; + + /* a lie, but we have to start somewhen */ + + last_sync = system_time() - 8333; + + ddprintf(("TDFXV3: 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(("TDFXV3: fake_interrupt_thread_func ends with flags = 0x%08lx\n", *flags)); + + /* gotta return something */ + + return B_OK; +} +#endif + +#if DEBUG > 0 +static int voodoodump(int argc, char **argv) { + unsigned int i; + + kprintf("VOODOO 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(("TDFXV3: 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; + } + + if (di->pcii.device_id==VOODOO3) { + si->pix_clk_max8 = 300000; + si->pix_clk_max16 = 300000; + si->pix_clk_max32 = 300000; + } else { + si->pix_clk_max8 = 270000; + si->pix_clk_max16 = 270000; + si->pix_clk_max32 = 270000; + } + + /* 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 VOODOOxx() macros */ + regs = di->regs; + + voodoo3_disableirq(); + /* 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)){ + ddprintf (("TDFXV3:faking IRQ\n")); + /* 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, voodoo_interrupt, (void *)di, 0); + /* bail if we couldn't install the handler */ + ddprintf (("TDFXV3:installed IRQ @ %i\n",di->pcii.u.h0.interrupt_line)); + 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(("TDFXV3: 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(("TDFXV3: 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(("TDFXV3: 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; + + voodoo3_disableirq(); + /* 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, voodoo_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(("TDFXV3: 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; + + switch (msg) + { + /* the only PUBLIC ioctl */ + case B_GET_ACCELERANT_SIGNATURE: + { + char *sig = (char *)buf; + strcpy(sig, "tdfx.accelerant"); + result = B_OK; + } + break; + + /* PRIVATE ioctl from here on */ + case VOODOO_GET_PRIVATE_DATA: + { + voodoo_get_private_data *gpd = (voodoo_get_private_data *)buf; + if (gpd->magic == VOODOO_PRIVATE_DATA_MAGIC) + { + gpd->shared_info_area = di->shared_area; + result = B_OK; + } + } + break; + case VOODOO_GET_PCI: + { + voodoo_get_set_pci *gsp = (voodoo_get_set_pci *)buf; + if (gsp->magic == VOODOO_PRIVATE_DATA_MAGIC) { + pci_info *pcii = &(di->pcii); + gsp->value = get_pci(gsp->offset, gsp->size); + result = B_OK; + } + } + break; + case VOODOO_SET_PCI: + { + voodoo_get_set_pci *gsp = (voodoo_get_set_pci *)buf; + if (gsp->magic == VOODOO_PRIVATE_DATA_MAGIC) { + pci_info *pcii = &(di->pcii); + set_pci(gsp->offset, gsp->size, gsp->value); + result = B_OK; + } + } + break; + case VOODOO_RUN_INTERRUPTS: + { + voodoo_set_bool_state *ri = (voodoo_set_bool_state *)buf; + if (ri->magic == VOODOO_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) { + voodoo3_enableirq(); + *regs = *regs; /* CHANGE ME */ + } else { + voodoo3_disableirq(); + *regs = *regs; /* CHANGE ME */ + } + } + result = B_OK; + } + } + break; + case VOODOO_DPRINTF: + dprintf ((char*)buf); + break; + + case VOODOO_INB: { + voodoo_port_io *io = (voodoo_port_io *)buf; + io->data8 = inportb(io->port); + } break; + case VOODOO_INW: { + voodoo_port_io *io = (voodoo_port_io *)buf; + io->data16=inportw(io->port); + } break; + case VOODOO_INL: { + voodoo_port_io *io = (voodoo_port_io *)buf; + io->data32= inportl(io->port); + } break; + case VOODOO_OUTB: { + voodoo_port_io *io = (voodoo_port_io *)buf; + outportb(io->port, io->data8); + } break; + case VOODOO_OUTW: { + voodoo_port_io *io = (voodoo_port_io *)buf; + outportw(io->port, io->data16); + + } break; + case VOODOO_OUTL: { + voodoo_port_io *io = (voodoo_port_io *)buf; + outportl(io->port, io->data32); + + } break; + } + return result; +}