ImÃport new 3dfx graphics driver from Gerald Zajac (see #6215.

Supports Banshee, Voodoo3 and Voodoo5 chips.
It will be promoted as older tdfx replacement soon, but not until 
my small changes around phys_addr_t are validated.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37241 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Philippe Houdoin 2010-06-24 03:47:49 +00:00
parent 98d65f5627
commit a56bc48835
21 changed files with 3192 additions and 1 deletions

View File

@ -0,0 +1,153 @@
/*
* Copyright 2007-2010 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
#ifndef DRIVERINTERFACE_H
#define DRIVERINTERFACE_H
#include <Accelerant.h>
#include <GraphicsDefs.h>
#include <Drivers.h>
#include <edid.h>
#include <video_overlay.h>
// This file contains info that is shared between the kernel driver and the
// accelerant, and info that is shared among the source files of the accelerant.
#define ENABLE_DEBUG_TRACE // if defined, turns on debug output to syslog
#define ARRAY_SIZE(a) (int(sizeof(a) / sizeof(a[0]))) // get number of elements in an array
struct Benaphore {
sem_id sem;
int32 count;
status_t Init(const char* name)
{
count = 0;
sem = create_sem(0, name);
return sem < 0 ? sem : B_OK;
}
status_t Acquire()
{
if (atomic_add(&count, 1) > 0)
return acquire_sem(sem);
return B_OK;
}
status_t Release()
{
if (atomic_add(&count, -1) > 1)
return release_sem(sem);
return B_OK;
}
void Delete() { delete_sem(sem); }
};
#define TDFX_PRIVATE_DATA_MAGIC 0x5042
enum {
TDFX_GET_SHARED_DATA = B_DEVICE_OP_CODES_END + 123,
TDFX_DEVICE_NAME,
TDFX_GET_PIO_REG,
TDFX_SET_PIO_REG
};
// Chip type numbers. These are used to group the chips into related
// groups. See table chipTable in driver.c
enum ChipType {
TDFX_NONE = 0,
BANSHEE,
VOODOO_3,
VOODOO_5,
};
struct PIORegInfo {
uint32 magic; // magic number
uint32 offset; // offset of register in PIO register area
int16 index; // index of value to read/write; < 0 if not indexed reg
uint8 value; // value to write or value that was read
};
struct DisplayModeEx : display_mode {
uint8 bitsPerPixel;
uint8 bytesPerPixel;
uint16 bytesPerRow; // number of bytes in one line/row
};
struct OverlayBuffer : overlay_buffer {
OverlayBuffer* nextBuffer; // pointer to next buffer in chain, NULL = none
uint32 size; // size of overlay buffer
};
struct SharedInfo {
// Device ID info.
uint16 vendorID; // PCI vendor ID, from pci_info
uint16 deviceID; // PCI device ID, from pci_info
uint8 revision; // PCI device revsion, from pci_info
ChipType chipType; // indicates group in which chip belongs (a group has similar functionality)
char chipName[32]; // user recognizable name of chip
bool bAccelerantInUse; // true = accelerant has been initialized
// Memory mappings.
area_id regsArea; // area_id for the memory mapped registers. It will
// be cloned into accelerant's address space.
area_id videoMemArea; // video memory area_id. The addresses are shared with all teams.
addr_t videoMemAddr; // video memory addr as viewed from virtual memory
phys_addr_t videoMemPCI; // video memory addr as viewed from the PCI bus (for DMA)
uint32 videoMemSize; // video memory size in bytes.
uint32 cursorOffset; // offset of cursor in video memory
uint32 frameBufferOffset; // offset of frame buffer in video memory
uint32 maxFrameBufferSize; // max available video memory for frame buffer
// Color spaces supported by current video chip/driver.
color_space colorSpaces[6];
uint32 colorSpaceCount; // number of color spaces in array colorSpaces
uint32 maxPixelClock; // max pixel clock of current chip in KHz
// List of screen modes.
area_id modeArea; // area containing list of display modes the driver supports
uint32 modeCount; // number of display modes in the list
DisplayModeEx displayMode; // current display mode configuration
uint16 cursorHotX; // Cursor hot spot. Top left corner of the cursor
uint16 cursorHotY; // is 0,0
edid1_info edidInfo;
bool bHaveEDID; // true = EDID info from device is in edidInfo
Benaphore engineLock; // for access to the acceleration engine
Benaphore overlayLock; // for overlay operations
int32 overlayAllocated; // non-zero if overlay is allocated
uint32 overlayToken;
OverlayBuffer* overlayBuffer; // pointer to linked list of buffers; NULL = none
};
#endif // DRIVERINTERFACE_H

View File

@ -0,0 +1,116 @@
/*
Copyright 2010 Haiku, Inc. All rights reserved.
Distributed under the terms of the MIT license.
Authors:
Gerald Zajac 2010
*/
#ifndef __3DFX_H__
#define __3DFX_H__
#define BIT(n) (1UL<<(n))
#define ROP_COPY 0xcc
#define ROP_INVERT 0x55
#define ROP_XOR 0x66
#define MEM_TYPE_SGRAM 0
#define MEM_TYPE_SDRAM 1
// Base register offsets in MMIO area.
#define STATUS 0x0
#define MISC_INIT1 0x14
#define DRAM_INIT0 0x18
#define DRAM_INIT1 0x1C
#define VGA_INIT0 0x28
#define VGA_INIT1 0x2c
#define PLL_CTRL0 0x40
#define DAC_MODE 0x4c
#define DAC_ADDR 0x50
#define DAC_DATA 0x54
#define RGB_MAX_DELTA 0x58
#define VIDEO_PROC_CONFIG 0x5c
#define HW_CURSOR_PAT_ADDR 0x60
#define HW_CURSOR_LOC 0x64
#define HW_CURSOR_COLOR0 0x68
#define HW_CURSOR_COLOR1 0x6c
#define VIDEO_SERIAL_PARALLEL_PORT 0x78
#define VIDEO_CHROMA_MIN 0x8c
#define VIDEO_CHROMA_MAX 0x90
#define VIDEO_SCREEN_SIZE 0x98
#define VIDEO_OVERLAY_START_COORDS 0x9c
#define VIDEO_OVERLAY_END_COORDS 0xa0
#define VIDEO_OVERLAY_DUDX 0xa4
#define VIDEO_OVERLAY_DUDX_OFFSET_SRC_WIDTH 0xa8
#define VIDEO_OVERLAY_DVDY 0xac
#define VIDEO_OVERLAY_DVDY_OFFSET 0xe0
#define VIDEO_DESKTOP_START_ADDR 0xe4
#define VIDEO_DESKTOP_OVERLAY_STRIDE 0xe8
#define VIDEO_IN_ADDR0 0xec
// Offset in MMIO area of registers used for drawing.
#define CLIP0_MIN (0x100000 + 0x8)
#define CLIP0_MAX (0x100000 + 0xC)
#define DST_BASE_ADDR (0x100000 + 0x10)
#define DST_FORMAT (0x100000 + 0x14)
#define SRC_BASE_ADDR (0x100000 + 0x34)
#define CLIP1_MIN (0x100000 + 0x4C)
#define CLIP1_MAX (0x100000 + 0x50)
#define SRC_FORMAT (0x100000 + 0x54)
#define SRC_XY (0x100000 + 0x5C)
#define COLOR_BACK (0x100000 + 0x60)
#define COLOR_FORE (0x100000 + 0x64)
#define DST_SIZE (0x100000 + 0x68)
#define DST_XY (0x100000 + 0x6C)
#define CMD_2D (0x100000 + 0x70)
// Offset in MMIO area of 3D registers.
#define CMD_3D (0x200000 + 0x120)
// Flags and register values.
#define CMD_2D_GO BIT(8)
#define CMD_3D_NOP 0
#define SGRAM_TYPE BIT(27)
#define SGRAM_NUM_CHIPSETS BIT(26)
#define DISABLE_2D_BLOCK_WRITE BIT(15)
#define MCTL_TYPE_SDRAM BIT(30)
#define DAC_MODE_2X BIT(0)
#define VIDEO_2X_MODE_ENABLE BIT(26)
#define CLUT_SELECT_8BIT BIT(2)
#define VGA0_EXTENSIONS BIT(6)
#define WAKEUP_3C3 BIT(8)
#define VGA0_LEGACY_DECODE BIT(9)
#define ENABLE_ALT_READBACK BIT(10)
#define EXT_SHIFT_OUT BIT(12)
#define VIDEO_PROCESSOR_ENABLE BIT(0)
#define DESKTOP_ENABLE BIT(7)
#define DESKTOP_PIXEL_FORMAT_SHIFT 18
#define DESKTOP_CLUT_BYPASS BIT(10)
#define OVERLAY_CLUT_BYPASS BIT(11)
#define CURSOR_ENABLE BIT(27)
#define STATUS_BUSY BIT(9)
#define X_RIGHT_TO_LEFT BIT(14)
#define Y_BOTTOM_TO_TOP BIT(15)
// 2D Commands
#define SCRN_TO_SCRN_BLIT 1
#define RECTANGLE_FILL 5
// Definitions for I2C bus when fetching EDID info.
#define VSP_SDA0_IN 0x00400000
#define VSP_SCL0_IN 0x00200000
#define VSP_SDA0_OUT 0x00100000
#define VSP_SCL0_OUT 0x00080000
#define VSP_ENABLE_IIC0 0x00040000 // set bit to 1 to enable I2C bus 0
// Definitions for overlays.
#define VIDEO_PROC_CONFIG_MASK 0xa2e3eb6c
#define VIDCFG_OVL_FMT_RGB565 (1 << 21)
#define VIDCFG_OVL_FMT_YUYV422 (5 << 21)
#endif // __3DFX_H__

View File

@ -0,0 +1,83 @@
/*
Copyright 2010 Haiku, Inc. All rights reserved.
Distributed under the terms of the MIT license.
Authors:
Gerald Zajac 2010
*/
#include "accelerant.h"
#include "3dfx.h"
#include <string.h>
void
TDFX_ShowCursor(bool bShow)
{
// Turn cursor on/off.
uint32 config = INREG32(VIDEO_PROC_CONFIG);
if (bShow)
config |= CURSOR_ENABLE;
else
config &= ~CURSOR_ENABLE;
TDFX_WaitForFifo(1);
OUTREG32(VIDEO_PROC_CONFIG, config);
}
void
TDFX_SetCursorPosition(int x, int y)
{
TDFX_WaitForFifo(1);
OUTREG32(HW_CURSOR_LOC, ((y + 63) << 16) | (x + 63));
}
bool
TDFX_LoadCursorImage(int width, int height, uint8* andMask, uint8* xorMask)
{
SharedInfo& si = *gInfo.sharedInfo;
if (andMask == NULL || xorMask == NULL)
return false;
uint64* fbCursor64 = (uint64*)((addr_t)si.videoMemAddr + si.cursorOffset);
// Initialize the hardware cursor as completely transparent.
for (int i = 0; i < CURSOR_BYTES; i += 16) {
*fbCursor64++ = ~0; // and bits
*fbCursor64++ = 0; // xor bits
}
// Now load the AND & XOR masks for the cursor image into the cursor
// buffer. Note that a particular bit in these masks will have the
// following effect upon the corresponding cursor pixel:
// AND XOR Result
// 0 0 White pixel
// 0 1 Black pixel
// 1 0 Screen color (for transparency)
// 1 1 Reverse screen color to black or white
uint8* fbCursor = (uint8*)((addr_t)si.videoMemAddr + si.cursorOffset);
for (int row = 0; row < height; row++) {
for (int colByte = 0; colByte < width / 8; colByte++) {
fbCursor[row * 16 + colByte] = *andMask++;
fbCursor[row * 16 + colByte + 8] = *xorMask++;
}
}
// Set the cursor colors which are white background and black foreground.
TDFX_WaitForFifo(2);
OUTREG32(HW_CURSOR_COLOR0, 0xffffff);
OUTREG32(HW_CURSOR_COLOR1, 0);
return true;
}

View File

@ -0,0 +1,87 @@
/*
Copyright 2010 Haiku, Inc. All rights reserved.
Distributed under the terms of the MIT license.
Authors:
Gerald Zajac 2010
*/
#include "accelerant.h"
#include "3dfx.h"
#define H_SYNC_OFF BIT(3)
#define V_SYNC_OFF BIT(1)
uint32
TDFX_DPMSCapabilities(void)
{
// Return DPMS modes supported by this device.
return B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF;
}
uint32
TDFX_GetDPMSMode(void)
{
// Return the current DPMS mode.
uint32 tmp = INREG32(DAC_MODE) & (H_SYNC_OFF | V_SYNC_OFF);
uint32 mode;
if (tmp == 0 )
mode = B_DPMS_ON;
else if (tmp == H_SYNC_OFF)
mode = B_DPMS_STAND_BY;
else if (tmp == V_SYNC_OFF)
mode = B_DPMS_SUSPEND;
else
mode = B_DPMS_OFF;
TRACE("TDFX_DPMSMode() mode: %d\n", mode);
return mode;
}
status_t
TDFX_SetDPMSMode(uint32 dpmsMode)
{
// Set the display into one of the Display Power Management modes,
// and return B_OK if successful, else return B_ERROR.
TRACE("TDFX_SetDPMSMode() mode: %d\n", dpmsMode);
uint32 dacMode = INREG32(DAC_MODE) & ~(H_SYNC_OFF | V_SYNC_OFF);
switch (dpmsMode) {
case B_DPMS_ON:
// Screen: On; HSync: On, VSync: On.
break;
case B_DPMS_STAND_BY:
// Screen: Off; HSync: Off, VSync: On.
dacMode |= H_SYNC_OFF;
break;
case B_DPMS_SUSPEND:
// Screen: Off; HSync: On, VSync: Off.
dacMode |= V_SYNC_OFF;
break;
case B_DPMS_OFF:
// Screen: Off; HSync: Off, VSync: Off.
dacMode |= H_SYNC_OFF | V_SYNC_OFF;
break;
default:
TRACE("Invalid DPMS mode %d\n", dpmsMode);
return B_ERROR;
}
OUTREG32(DAC_MODE, dacMode);
return B_OK;
}

View File

@ -0,0 +1,152 @@
/*
Copyright 2010 Haiku, Inc. All rights reserved.
Distributed under the terms of the MIT license.
Authors:
Gerald Zajac
*/
#include "accelerant.h"
#include "3dfx.h"
// Constants for the DST_FORMAT register based upon the color depth.
static const uint32 fmtColorDepth[] = {
0x10000, // 1 byte/pixel
0x30000, // 2 bytes/pixel
0x50000, // 3 bytes/pixel (not used)
0x50000 // 4 bytes/pixel
};
void
TDFX_FillRectangle(engine_token *et, uint32 color, fill_rect_params *list, uint32 count)
{
(void)et; // avoid compiler warning for unused arg
DisplayModeEx& mode = gInfo.sharedInfo->displayMode;
uint32 fmt = mode.bytesPerRow | fmtColorDepth[mode.bytesPerPixel - 1];
TDFX_WaitForFifo(3);
OUTREG32(DST_FORMAT, fmt);
OUTREG32(COLOR_BACK, color);
OUTREG32(COLOR_FORE, color);
while (count--) {
int x = list->left;
int y = list->top;
int w = list->right - x + 1;
int h = list->bottom - y + 1;
TDFX_WaitForFifo(3);
OUTREG32(DST_SIZE, w | (h << 16));
OUTREG32(DST_XY, x | (y << 16));
OUTREG32(CMD_2D, RECTANGLE_FILL | CMD_2D_GO | (ROP_COPY << 24));
list++;
}
}
void
TDFX_FillSpan(engine_token *et, uint32 color, uint16 *list, uint32 count)
{
(void)et; // avoid compiler warning for unused arg
DisplayModeEx& mode = gInfo.sharedInfo->displayMode;
uint32 fmt = mode.bytesPerRow | fmtColorDepth[mode.bytesPerPixel - 1];
TDFX_WaitForFifo(3);
OUTREG32(DST_FORMAT, fmt);
OUTREG32(COLOR_BACK, color);
OUTREG32(COLOR_FORE, color);
while (count--) {
int y = *list++;
int x = *list++;
int w = *list++ - x + 1;
if (w <= 0)
continue; // discard span with zero or negative width
TDFX_WaitForFifo(3);
OUTREG32(DST_SIZE, w | (1 << 16));
OUTREG32(DST_XY, x | (y << 16));
OUTREG32(CMD_2D, RECTANGLE_FILL | CMD_2D_GO | (ROP_COPY << 24));
}
}
void
TDFX_InvertRectangle(engine_token *et, fill_rect_params *list, uint32 count)
{
(void)et; // avoid compiler warning for unused arg
DisplayModeEx& mode = gInfo.sharedInfo->displayMode;
uint32 fmt = mode.bytesPerRow | fmtColorDepth[mode.bytesPerPixel - 1];
TDFX_WaitForFifo(1);
OUTREG32(DST_FORMAT, fmt);
while (count--) {
int x = list->left;
int y = list->top;
int w = list->right - x + 1;
int h = list->bottom - y + 1;
TDFX_WaitForFifo(3);
OUTREG32(DST_SIZE, w | (h << 16));
OUTREG32(DST_XY, x | (y << 16));
OUTREG32(CMD_2D, RECTANGLE_FILL | CMD_2D_GO | (ROP_INVERT << 24));
list++;
}
}
void
TDFX_ScreenToScreenBlit(engine_token *et, blit_params *list, uint32 count)
{
(void)et; // avoid compiler warning for unused arg
DisplayModeEx& mode = gInfo.sharedInfo->displayMode;
uint32 fmt = mode.bytesPerRow | fmtColorDepth[mode.bytesPerPixel - 1];
TDFX_WaitForFifo(2);
OUTREG32(DST_FORMAT, fmt);
OUTREG32(SRC_FORMAT, fmt);
while (count--) {
int src_x = list->src_left;
int src_y = list->src_top;
int dest_x = list->dest_left;
int dest_y = list->dest_top;
int width = list->width;
int height = list->height;
uint32 cmd = SCRN_TO_SCRN_BLIT | CMD_2D_GO | (ROP_COPY << 24);
if (src_x <= dest_x) {
cmd |= X_RIGHT_TO_LEFT;
src_x += width;
dest_x += width;
}
if (src_y <= dest_y) {
cmd |= Y_BOTTOM_TO_TOP;
src_y += height;
dest_y += height;
}
TDFX_WaitForFifo(4);
OUTREG32(SRC_XY, src_x | (src_y << 16));
OUTREG32(DST_SIZE, (width + 1) | ((height + 1) << 16));
OUTREG32(DST_XY, dest_x | (dest_y << 16));
OUTREG32(CMD_2D, cmd);
list++;
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 2010 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
#include "accelerant.h"
#include "3dfx.h"
#include <string.h>
#include <ddc.h>
#include <edid.h>
static status_t
GetI2CSignals(void* cookie, int* _clock, int* data)
{
(void)cookie; // avoid compiler warning for unused arg
uint32 reg = INREG32(VIDEO_SERIAL_PARALLEL_PORT);
*_clock = (reg & VSP_SCL0_IN) ? 1 : 0;;
*data = (reg & VSP_SDA0_IN) ? 1 : 0;
return B_OK;
}
static status_t
SetI2CSignals(void* cookie, int _clock, int data)
{
(void)cookie; // avoid compiler warning for unused arg
uint32 reg = (INREG32(VIDEO_SERIAL_PARALLEL_PORT)
& ~(VSP_SDA0_OUT | VSP_SCL0_OUT));
reg = (reg | (_clock ? VSP_SCL0_OUT : 0) | (data ? VSP_SDA0_OUT : 0));
OUTREG32(VIDEO_SERIAL_PARALLEL_PORT, reg);
return B_OK;
}
bool
TDFX_GetEdidInfo(edid1_info& edidInfo)
{
// Get the EDID info and return true if successful.
i2c_bus bus;
bus.cookie = (void*)NULL;
bus.set_signals = &SetI2CSignals;
bus.get_signals = &GetI2CSignals;
ddc2_init_timing(&bus);
uint32 reg = INREG32(VIDEO_SERIAL_PARALLEL_PORT);
OUTREG32(VIDEO_SERIAL_PARALLEL_PORT, reg | VSP_ENABLE_IIC0);
bool bResult = (ddc2_read_edid1(&bus, &edidInfo, NULL, NULL) == B_OK);
OUTREG32(VIDEO_SERIAL_PARALLEL_PORT, reg);
return bResult;
}

View File

@ -0,0 +1,89 @@
/*
* Copyright 2010 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
#include "accelerant.h"
#include "3dfx.h"
uint32
TDFX_GetVideoMemorySize(void)
{
// Return the number of bytes of video memory.
uint32 chipSize; // size is in megabytes
uint32 dramInit0 = INREG32(DRAM_INIT0);
uint32 dramInit1 = INREG32(DRAM_INIT1);
uint32 numChips = (dramInit0 & SGRAM_NUM_CHIPSETS) ? 8 : 4;
int memType = (dramInit1 & MCTL_TYPE_SDRAM) ? MEM_TYPE_SDRAM : MEM_TYPE_SGRAM;
if (gInfo.sharedInfo->chipType == VOODOO_5) {
chipSize = 1 << ((dramInit0 >> 27) & 0x7);
} else {
// Banshee or Voodoo3
if (memType == MEM_TYPE_SDRAM)
chipSize = 2;
else
chipSize = (dramInit0 & SGRAM_TYPE) ? 2 : 1;
}
// Disable block writes for SDRAM.
uint32 miscInit1 = INREG32(MISC_INIT1);
if (memType == MEM_TYPE_SDRAM) {
miscInit1 |= DISABLE_2D_BLOCK_WRITE;
}
miscInit1 |= 1;
OUTREG32(MISC_INIT1, miscInit1);
return chipSize * numChips * 1024 * 1024;
}
status_t
TDFX_Init(void)
{
TRACE("TDFX_Init()\n");
SharedInfo& si = *gInfo.sharedInfo;
si.videoMemSize = TDFX_GetVideoMemorySize();
si.cursorOffset = 0;
si.frameBufferOffset = si.cursorOffset + CURSOR_BYTES;
si.maxFrameBufferSize = si.videoMemSize - si.frameBufferOffset;
TRACE("Video Memory size: %d MB\n", si.videoMemSize / 1024 / 1024);
TRACE("frameBufferOffset: 0x%x cursorOffset: 0x%x\n", si.frameBufferOffset, si.cursorOffset);
switch (si.chipType) {
case BANSHEE:
si.maxPixelClock = 270000;
break;
case VOODOO_3:
si.maxPixelClock = 300000;
break;
case VOODOO_5:
si.maxPixelClock = 350000;
break;
default:
TRACE("Undefined chip type: %d\n", si.chipType);
return B_ERROR;
}
// Set up the array of color spaces supported by the 3dfx chips.
si.colorSpaces[0] = B_CMAP8;
si.colorSpaces[1] = B_RGB16;
si.colorSpaces[2] = B_RGB32;
si.colorSpaceCount = 3;
// Setup the mode list.
return CreateModeList(IsModeUsable);
}

View File

@ -0,0 +1,402 @@
/*
Copyright 2010 Haiku, Inc. All rights reserved.
Distributed under the terms of the MIT license.
Authors:
Gerald Zajac
*/
/*
Some of the code in this source file was adapted from the X.org tdfx
video driver, and was covered by the following copyright and license.
--------------------------------------------------------------------------
Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sub license, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice (including the
next paragraph) shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "accelerant.h"
#include "3dfx.h"
#include <stdlib.h>
#include <unistd.h>
// Functions to read/write PIO registers.
//=======================================
static void
WritePIOReg(uint32 offset, int16 index, uint8 value)
{
PIORegInfo prInfo;
prInfo.magic = TDFX_PRIVATE_DATA_MAGIC;
prInfo.offset = offset;
prInfo.index = index;
prInfo.value = value;
status_t result = ioctl(gInfo.deviceFileDesc, TDFX_SET_PIO_REG,
&prInfo, sizeof(prInfo));
if (result != B_OK)
TRACE("WritePIOReg() failed, result = 0x%x\n", result);
}
static uint8
ReadPIOReg(uint32 offset, int16 index)
{
PIORegInfo prInfo;
prInfo.magic = TDFX_PRIVATE_DATA_MAGIC;
prInfo.offset = offset;
prInfo.index = index;
status_t result = ioctl(gInfo.deviceFileDesc, TDFX_GET_PIO_REG,
&prInfo, sizeof(prInfo));
if (result != B_OK)
TRACE("ReadPIOReg() failed, result = 0x%x\n", result);
return prInfo.value;
}
static void
WriteMiscOutReg(uint8 value)
{
WritePIOReg(MISC_OUT_W - 0x300, -1, value);
}
static void
WriteCrtcReg(uint8 index, uint8 value)
{
WritePIOReg(CRTC_INDEX - 0x300, index, value);
}
uint8
ReadMiscOutReg()
{
return ReadPIOReg(MISC_OUT_R - 0x300, -1);
}
uint8
ReadCrtcReg(uint8 index)
{
return ReadPIOReg(CRTC_INDEX - 0x300, index);
}
void
TDFX_WaitForFifo(uint32 entries)
{
// The FIFO has 32 slots. This routine waits until at least `entries'
// of these slots are empty.
while ((INREG32(STATUS) & 0x1f) < entries) ;
}
void
TDFX_WaitForIdle()
{
// Wait for the graphics engine to be completely idle.
TDFX_WaitForFifo(1);
OUTREG32(CMD_3D, CMD_3D_NOP);
int i = 0;
do {
i = (INREG32(STATUS) & STATUS_BUSY) ? 0 : i + 1;
} while (i < 3);
}
static int
TDFX_CalcPLL(int freq)
{
const int refFreq = 14318;
int best_error = freq;
int best_k = 0;
int best_m = 0;
int best_n = 0;
for (int n = 1; n < 256; n++) {
int freqCur = refFreq * (n + 2);
if (freqCur < freq) {
freqCur = freqCur / 3;
if (freq - freqCur < best_error) {
best_error = freq - freqCur;
best_n = n;
best_m = 1;
best_k = 0;
continue;
}
}
for (int m = 1; m < 64; m++) {
for (int k = 0; k < 4; k++) {
freqCur = refFreq * (n + 2) / (m + 2) / (1 << k);
if (abs(freqCur - freq) < best_error) {
best_error = abs(freqCur - freq);
best_n = n;
best_m = m;
best_k = k;
}
}
}
}
return (best_n << 8) | (best_m << 2) | best_k;
}
bool
TDFX_GetColorSpaceParams(int colorSpace, uint8& bitsPerPixel)
{
// Get parameters for a color space which is supported by the 3dfx chips.
// Return true if the color space is supported; else return false.
switch (colorSpace) {
case B_RGB32:
bitsPerPixel = 32;
break;
case B_RGB16:
bitsPerPixel = 16;
break;
case B_RGB15:
bitsPerPixel = 15;
break;
case B_CMAP8:
bitsPerPixel = 8;
break;
default:
TRACE("Unsupported color space: 0x%X\n", colorSpace);
return false;
}
return true;
}
status_t
TDFX_SetDisplayMode(const DisplayModeEx& mode)
{
// The code to actually configure the display.
// All the error checking must be done in ProposeDisplayMode(),
// and assume that the mode values we get here are acceptable.
SharedInfo& si = *gInfo.sharedInfo;
bool clock2X = mode.timing.pixel_clock > si.maxPixelClock / 2;
// Initialize the timing values for CRTC registers cr00 to cr18. Note
// that the number following the letters 'cr' is a hexadecimal number.
// Argument crtc will contain registers cr00 to cr18; thus, it must
// contain at least 25 (0x19) elements.
// Normally the horizontal timing values are divided by 8; however,
// if the clock is above the 2X value, divide by 16 such that the values
// are halved.
int horzDiv = clock2X ? 16 : 8;
int hTotal = mode.timing.h_total / horzDiv - 5;
int hDisp_e = mode.timing.h_display / horzDiv - 1;
int hSync_s = mode.timing.h_sync_start / horzDiv;
int hSync_e = mode.timing.h_sync_end / horzDiv;
int hBlank_s = hDisp_e + 1; // start of horizontal blanking
int hBlank_e = hTotal + 3; // end of horizontal blanking
int vTotal = mode.timing.v_total - 2;
int vDisp_e = mode.timing.v_display - 1;
int vSync_s = mode.timing.v_sync_start;
int vSync_e = mode.timing.v_sync_end;
int vBlank_s = vDisp_e + 1; // start of vertical blanking
int vBlank_e = vTotal; // end of vertical blanking
// CRTC Controller values
uint8 crtc[25];
crtc[0x00] = hTotal;
crtc[0x01] = hDisp_e;
crtc[0x02] = hBlank_s;
crtc[0x03] = (hBlank_e & 0x1f) | 0x80;
crtc[0x04] = hSync_s;
crtc[0x05] = ((hSync_e & 0x1f) | ((hBlank_e & 0x20) << 2));
crtc[0x06] = vTotal;
crtc[0x07] = (((vTotal & 0x100) >> 8)
| ((vDisp_e & 0x100) >> 7)
| ((vSync_s & 0x100) >> 6)
| ((vBlank_s & 0x100) >> 5)
| 0x10
| ((vTotal & 0x200) >> 4)
| ((vDisp_e & 0x200) >> 3)
| ((vSync_s & 0x200) >> 2));
crtc[0x08] = 0x00;
crtc[0x09] = ((vBlank_s & 0x200) >> 4) | 0x40;
crtc[0x0a] = 0x00;
crtc[0x0b] = 0x00;
crtc[0x0c] = 0x00;
crtc[0x0d] = 0x00;
crtc[0x0e] = 0x00;
crtc[0x0f] = 0x00;
crtc[0x10] = vSync_s;
crtc[0x11] = (vSync_e & 0x0f) | 0x20;
crtc[0x12] = vDisp_e;
crtc[0x13] = hDisp_e + 1;
crtc[0x14] = 0x00;
crtc[0x15] = vBlank_s;
crtc[0x16] = vBlank_e;
crtc[0x17] = 0xc3;
crtc[0x18] = 0xff;
// Set up the extra CR reg's to handle the higher resolution modes.
uint8 cr1a = (hTotal & 0x100) >> 8
| (hDisp_e & 0x100) >> 6
| (hBlank_s & 0x100) >> 4
| (hBlank_e & 0x40) >> 1
| (hSync_s & 0x100) >> 2
| (hSync_e & 0x20) << 2;
uint8 cr1b = (vTotal & 0x400) >> 10
| (vDisp_e & 0x400) >> 8
| (vBlank_s & 0x400) >> 6
| (vBlank_e & 0x400) >> 4;
uint8 miscOutReg = 0x0f | (mode.timing.v_display < 400 ? 0xa0
: mode.timing.v_display < 480 ? 0x60
: mode.timing.v_display < 768 ? 0xe0 : 0x20);
uint32 vgaInit0 = VGA0_EXTENSIONS
| WAKEUP_3C3
| ENABLE_ALT_READBACK
| CLUT_SELECT_8BIT
| EXT_SHIFT_OUT;
uint32 videoConfig = VIDEO_PROCESSOR_ENABLE | DESKTOP_ENABLE
| (mode.bytesPerPixel - 1) << DESKTOP_PIXEL_FORMAT_SHIFT
| (mode.bytesPerPixel > 1 ? DESKTOP_CLUT_BYPASS : 0);
uint32 dacMode = INREG32(DAC_MODE) & ~DAC_MODE_2X;
if (clock2X) {
dacMode |= DAC_MODE_2X;
videoConfig |= VIDEO_2X_MODE_ENABLE;
}
uint32 pllFreq = TDFX_CalcPLL(mode.timing.pixel_clock);
// Note that for the Banshee chip, the mode 1280x1024 at 60Hz refresh does
// not display properly using the computed PLL frequency; thus, set it to
// the value that is computed when set by VESA.
if (si.chipType == BANSHEE && pllFreq == 45831
&& mode.timing.h_display == 1280 && mode.timing.v_display == 1024)
pllFreq = 45912;
uint32 screenSize = mode.timing.h_display | (mode.timing.v_display << 12);
// Now that the values for the registers have been computed, write the
// registers to set the mode.
//=====================================================================
TDFX_WaitForFifo(2);
OUTREG32(VIDEO_PROC_CONFIG, 0);
OUTREG32(PLL_CTRL0, pllFreq);
WriteMiscOutReg(miscOutReg);
for (uint8 j = 0; j < 25; j++)
WriteCrtcReg(j, crtc[j]);
WriteCrtcReg(0x1a, cr1a);
WriteCrtcReg(0x1b, cr1b);
TDFX_WaitForFifo(6);
OUTREG32(VGA_INIT0, vgaInit0);
OUTREG32(DAC_MODE, dacMode);
OUTREG32(VIDEO_DESKTOP_OVERLAY_STRIDE, mode.bytesPerRow);
OUTREG32(HW_CURSOR_PAT_ADDR, si.cursorOffset);
OUTREG32(VIDEO_SCREEN_SIZE, screenSize);
OUTREG32(VIDEO_DESKTOP_START_ADDR, si.frameBufferOffset);
TDFX_WaitForFifo(7);
OUTREG32(CLIP0_MIN, 0);
OUTREG32(CLIP0_MAX, 0x0fff0fff);
OUTREG32(CLIP1_MIN, 0);
OUTREG32(CLIP1_MAX, 0x0fff0fff);
OUTREG32(VIDEO_PROC_CONFIG, videoConfig);
OUTREG32(SRC_BASE_ADDR, si.frameBufferOffset);
OUTREG32(DST_BASE_ADDR, si.frameBufferOffset);
TDFX_WaitForIdle();
TDFX_AdjustFrame(mode);
return B_OK;
}
void
TDFX_AdjustFrame(const DisplayModeEx& mode)
{
// Adjust start address in frame buffer.
SharedInfo& si = *gInfo.sharedInfo;
int address = (mode.v_display_start * mode.virtual_width
+ mode.h_display_start) * mode.bytesPerPixel;
address &= ~0x07;
address += si.frameBufferOffset;
TDFX_WaitForFifo(1);
OUTREG32(VIDEO_DESKTOP_START_ADDR, address);
return;
}
void
TDFX_SetIndexedColors(uint count, uint8 first, uint8* colorData, uint32 flags)
{
// Set the indexed color palette for 8-bit color depth mode.
(void)flags; // avoid compiler warning for unused arg
if (gInfo.sharedInfo->displayMode.space != B_CMAP8)
return ;
uint32 index = first;
while (count--) {
uint32 color = ((colorData[0] << 16) | (colorData[1] << 8) | colorData[2]);
TDFX_WaitForFifo(2);
OUTREG32(DAC_ADDR, index++);
INREG32(DAC_ADDR); // color not always set unless we read after write
OUTREG32(DAC_DATA, color);
colorData += 3;
}
}

View File

@ -0,0 +1,121 @@
/*
* Copyright 2010 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
#include "accelerant.h"
#include "3dfx.h"
bool
TDFX_DisplayOverlay(const overlay_window* window,
const overlay_buffer* buffer,
const overlay_view* view)
{
// Return true if setup is successful.
SharedInfo& si = *gInfo.sharedInfo;
if (window == NULL || buffer == NULL || view == NULL)
return false;
if (window->flags & B_OVERLAY_COLOR_KEY) {
uint32 color = 0;
if (si.displayMode.bitsPerPixel == 16) {
color = (window->blue.value & window->blue.mask) << 0
| (window->green.value & window->green.mask) << 5
| (window->red.value & window->red.mask) << 11;
// 16 bit color has no alpha bits
} else {
color = (window->blue.value & window->blue.mask) << 0
| (window->green.value & window->green.mask) << 8
| (window->red.value & window->red.mask) << 16
| (window->alpha.value & window->alpha.mask) << 24;
}
TDFX_WaitForFifo(2);
OUTREG32(VIDEO_CHROMA_MIN, color);
OUTREG32(VIDEO_CHROMA_MAX, color);
}
uint32 videoConfig = INREG32(VIDEO_PROC_CONFIG);
videoConfig &= ~VIDEO_PROC_CONFIG_MASK;
videoConfig |= (0x00000320 | OVERLAY_CLUT_BYPASS);
// Scale image if window dimension is larger than the buffer dimension.
// Scaling is not done if window dimension is smaller since the chip only
// scales up to a larger dimension, and does not scale down to a smaller
// dimension.
if (window->width > buffer->width)
videoConfig |= (1 << 14);
if (window->height > buffer->height)
videoConfig |= (1 << 15);
switch (buffer->space) {
case B_YCbCr422:
videoConfig |= VIDCFG_OVL_FMT_YUYV422;
break;
case B_RGB16:
videoConfig |= VIDCFG_OVL_FMT_RGB565;
break;
default:
return false; // color space not supported
}
// can't do bilinear filtering when in 2X mode
if ((videoConfig & VIDEO_2X_MODE_ENABLE) == 0)
videoConfig |= (3 << 16);
TDFX_WaitForFifo(1);
OUTREG32(VIDEO_PROC_CONFIG, videoConfig);
// Subtract 1 from height to eliminate junk on last line of image.
int32 dudx = (buffer->width << 20) / window->width;
int32 dudy = ((buffer->height - 1) << 20) / window->height;
int32 x1 = (window->h_start < 0) ? 0 : window->h_start;
int32 y1 = (window->v_start < 0) ? 0 : window->v_start;
int32 x2 = x1 + window->width - 1;
int32 y2 = y1 + window->height - 1;
TDFX_WaitForFifo(6);
// Set up coordinates of overlay window on screen.
OUTREG32(VIDEO_OVERLAY_START_COORDS, x1 | (y1 << 12));
OUTREG32(VIDEO_OVERLAY_END_COORDS, x2 | (y2 << 12));
// Set up scale and position of overlay in graphics memory.
OUTREG32(VIDEO_OVERLAY_DUDX, dudx);
OUTREG32(VIDEO_OVERLAY_DUDX_OFFSET_SRC_WIDTH, ((x1 & 0x0001ffff) << 3)
| (buffer->width << 20));
OUTREG32(VIDEO_OVERLAY_DVDY, dudy);
OUTREG32(VIDEO_OVERLAY_DVDY_OFFSET, (y1 & 0x0000ffff) << 3);
// Add width of overlay buffer to stride.
uint32 stride = INREG32(VIDEO_DESKTOP_OVERLAY_STRIDE) & 0x0000ffff;
stride |= (buffer->width << 1) << 16;
uint32 offset = (uint32)buffer->buffer_dma;
TDFX_WaitForFifo(2);
OUTREG32(VIDEO_DESKTOP_OVERLAY_STRIDE, stride);
OUTREG32(VIDEO_IN_ADDR0, offset);
return true;
}
void
TDFX_StopOverlay(void)
{
// reset the video
uint32 videoConfig = INREG32(VIDEO_PROC_CONFIG) & ~VIDEO_PROC_CONFIG_MASK;
OUTREG32(VIDEO_PROC_CONFIG, videoConfig);
OUTREG32(RGB_MAX_DELTA, 0x0080808);
}

View File

@ -0,0 +1,24 @@
SubDir HAIKU_TOP src add-ons accelerants 3dfx ;
UsePrivateHeaders graphics ;
UsePrivateHeaders [ FDirName graphics 3dfx ] ;
UsePrivateHeaders [ FDirName graphics common ] ;
Addon 3dfx.accelerant :
accelerant.cpp
cursor.cpp
engine.cpp
hooks.cpp
mode.cpp
overlay.cpp
3dfx_cursor.cpp
3dfx_dpms.cpp
3dfx_draw.cpp
3dfx_edid.cpp
3dfx_init.cpp
3dfx_mode.cpp
3dfx_overlay.cpp
: be libaccelerantscommon.a
;

View File

@ -0,0 +1,196 @@
/*
* Copyright 2007-2010 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
#include "accelerant.h"
#include <errno.h>
#include <string.h>
#include <unistd.h>
AccelerantInfo gInfo; // global data used by various source files of accelerant.
static status_t
InitCommon(int fileDesc)
{
// Initialization function used by primary and cloned accelerants.
gInfo.deviceFileDesc = fileDesc;
// Get area ID of shared data from driver.
area_id sharedArea;
status_t result = ioctl(gInfo.deviceFileDesc, TDFX_GET_SHARED_DATA,
&sharedArea, sizeof(sharedArea));
if (result != B_OK)
return result;
gInfo.sharedInfoArea = clone_area("3DFX shared info", (void**)&(gInfo.sharedInfo),
B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, sharedArea);
if (gInfo.sharedInfoArea < 0)
return gInfo.sharedInfoArea; // sharedInfoArea has error code
gInfo.regsArea = clone_area("3DFX regs area", (void**)&(gInfo.regs),
B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, gInfo.sharedInfo->regsArea);
if (gInfo.regsArea < 0) {
delete_area(gInfo.sharedInfoArea);
return gInfo.regsArea; // regsArea has error code
}
return B_OK;
}
static void
UninitCommon(void)
{
// This function is used by both primary and cloned accelerants.
delete_area(gInfo.regsArea);
gInfo.regs = 0;
delete_area(gInfo.sharedInfoArea);
gInfo.sharedInfo = 0;
}
status_t
InitAccelerant(int fileDesc)
{
// Initialize the accelerant. fileDesc is the file handle of the device
// (in /dev/graphics) that has been opened by the app_server.
TRACE("Enter InitAccelerant()\n");
gInfo.bAccelerantIsClone = false; // indicate this is primary accelerant
status_t result = InitCommon(fileDesc);
if (result == B_OK) {
SharedInfo& si = *gInfo.sharedInfo;
TRACE("Vendor ID: 0x%X, Device ID: 0x%X\n", si.vendorID, si.deviceID);
// Ensure that InitAccelerant is executed just once (copies should be clones)
if (si.bAccelerantInUse) {
result = B_NOT_ALLOWED;
} else {
result = TDFX_Init(); // perform init related to current chip
if (result == B_OK) {
result = si.engineLock.Init("3DFX engine lock");
if (result == B_OK)
result = si.overlayLock.Init("3DFX overlay lock");
if (result == B_OK) {
TDFX_ShowCursor(false);
// ensure that this function won't be executed again
// (copies should be clones)
si.bAccelerantInUse = true;
}
}
}
if (result != B_OK)
UninitCommon();
}
TRACE("Leave InitAccelerant(), result: 0x%X\n", result);
return result;
}
ssize_t
AccelerantCloneInfoSize(void)
{
// Return the number of bytes required to hold the information required
// to clone the device. The information is merely the name of the device;
// thus, return the size of the name buffer.
return B_OS_NAME_LENGTH;
}
void
GetAccelerantCloneInfo(void* data)
{
// Return the info required to clone the device. Argument data points to
// a buffer which is the size returned by AccelerantCloneInfoSize().
ioctl(gInfo.deviceFileDesc, TDFX_DEVICE_NAME, data, B_OS_NAME_LENGTH);
}
status_t
CloneAccelerant(void* data)
{
// Initialize a copy of the accelerant as a clone. Argument data points to
// a copy of the data which was returned by GetAccelerantCloneInfo().
TRACE("Enter CloneAccelerant()\n");
char path[MAXPATHLEN] = "/dev/";
strcat(path, (const char*)data);
gInfo.deviceFileDesc = open(path, B_READ_WRITE); // open the device
if (gInfo.deviceFileDesc < 0)
return errno;
gInfo.bAccelerantIsClone = true;
status_t result = InitCommon(gInfo.deviceFileDesc);
if (result != B_OK) {
close(gInfo.deviceFileDesc);
return result;
}
result = gInfo.modeListArea = clone_area("3DFX cloned display_modes",
(void**) &gInfo.modeList, B_ANY_ADDRESS, B_READ_AREA,
gInfo.sharedInfo->modeArea);
if (result < 0) {
UninitCommon();
close(gInfo.deviceFileDesc);
return result;
}
TRACE("Leave CloneAccelerant()\n");
return B_OK;
}
void
UninitAccelerant(void)
{
delete_area(gInfo.modeListArea);
gInfo.modeList = NULL;
UninitCommon();
if (gInfo.bAccelerantIsClone)
close(gInfo.deviceFileDesc);
}
status_t
GetAccelerantDeviceInfo(accelerant_device_info* adi)
{
// Get info about the device.
SharedInfo& si = *gInfo.sharedInfo;
adi->version = 1;
strcpy(adi->name, "3dfx chipset");
strcpy(adi->chipset, si.chipName);
strcpy(adi->serial_no, "unknown");
adi->memory = si.maxFrameBufferSize;
adi->dac_speed = 270;
return B_OK;
}

View File

@ -0,0 +1,186 @@
/*
* Copyright 2007-2010 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
#ifndef _ACCELERANT_H
#define _ACCELERANT_H
#include "DriverInterface.h"
#undef TRACE
#ifdef ENABLE_DEBUG_TRACE
extern "C" void _sPrintf(const char* format, ...);
# define TRACE(x...) _sPrintf("3dfx: " x)
#else
# define TRACE(x...) ;
#endif
#define CURSOR_BYTES 1024 // bytes used for cursor image in video memory
// Global data used by various source files of the accelerant.
struct AccelerantInfo {
int deviceFileDesc; // file descriptor of kernel driver
SharedInfo* sharedInfo; // address of info shared between accelerants & driver
area_id sharedInfoArea; // shared info area ID
uint8* regs; // base address of MMIO register area
area_id regsArea; // MMIO register area ID
display_mode* modeList; // list of standard display modes
area_id modeListArea; // mode list area ID
bool bAccelerantIsClone; // true if this is a cloned accelerant
};
extern AccelerantInfo gInfo;
// Prototypes of the interface functions called by the app_server. Note that
// the functions that are unique to a particular chip family, will be prefixed
// with the name of the family, and the functions that are applicable to all
// chips will have no prefix.
//================================================================
#if defined(__cplusplus)
extern "C" {
#endif
// General
status_t InitAccelerant(int fd);
ssize_t AccelerantCloneInfoSize(void);
void GetAccelerantCloneInfo(void* data);
status_t CloneAccelerant(void* data);
void UninitAccelerant(void);
status_t GetAccelerantDeviceInfo(accelerant_device_info* adi);
// Mode Configuration
uint32 AccelerantModeCount(void);
status_t GetModeList(display_mode* dm);
status_t ProposeDisplayMode(display_mode* target, const display_mode* low,
const display_mode* high);
status_t SetDisplayMode(display_mode* mode_to_set);
status_t GetDisplayMode(display_mode* current_mode);
status_t GetFrameBufferConfig(frame_buffer_config* a_frame_buffer);
status_t GetPixelClockLimits(display_mode* dm, uint32* low, uint32* high);
status_t MoveDisplay(uint16 h_display_start, uint16 v_display_start);
void TDFX_SetIndexedColors(uint count, uint8 first, uint8* color_data,
uint32 flags);
status_t GetEdidInfo(void* info, size_t size, uint32* _version);
// DPMS
uint32 TDFX_DPMSCapabilities(void);
uint32 TDFX_GetDPMSMode(void);
status_t TDFX_SetDPMSMode(uint32 dpms_flags);
// Cursor
status_t SetCursorShape(uint16 width, uint16 height, uint16 hot_x, uint16 hot_y,
uint8* andMask, uint8* xorMask);
void MoveCursor(uint16 x, uint16 y);
void TDFX_ShowCursor(bool bShow);
// Engine Management
uint32 AccelerantEngineCount(void);
status_t AcquireEngine(uint32 capabilities, uint32 max_wait, sync_token* st,
engine_token** et);
status_t ReleaseEngine(engine_token* et, sync_token* st);
void WaitEngineIdle(void);
status_t GetSyncToken(engine_token* et, sync_token* st);
status_t SyncToToken(sync_token* st);
// 2D acceleration
void TDFX_FillRectangle(engine_token* et, uint32 color,
fill_rect_params* list, uint32 count);
void TDFX_FillSpan(engine_token* et, uint32 color, uint16* list, uint32 count);
void TDFX_InvertRectangle(engine_token* et, fill_rect_params* list, uint32 count);
void TDFX_ScreenToScreenBlit(engine_token* et, blit_params* list, uint32 count);
// Video_overlay
uint32 OverlayCount(const display_mode *dm);
const uint32* OverlaySupportedSpaces(const display_mode *dm);
uint32 OverlaySupportedFeatures(uint32 a_color_space);
const overlay_buffer* AllocateOverlayBuffer(color_space cs, uint16 width,
uint16 height);
status_t ReleaseOverlayBuffer(const overlay_buffer *ob);
status_t GetOverlayConstraints(const display_mode *dm,
const overlay_buffer *ob, overlay_constraints *oc);
overlay_token AllocateOverlay(void);
status_t ReleaseOverlay(overlay_token ot);
status_t ConfigureOverlay(overlay_token ot, const overlay_buffer *ob,
const overlay_window *ow, const overlay_view *ov);
#if defined(__cplusplus)
}
#endif
// Prototypes for other functions that are called from source files other than
// where they are defined.
//============================================================================
status_t CreateModeList(bool (*checkMode)(const display_mode* mode));
uint16 GetVesaModeNumber(const display_mode& mode, uint8 bitsPerPixel);
bool IsModeUsable(const display_mode* mode);
// 3dfx functions.
bool TDFX_DisplayOverlay(const overlay_window* window,
const overlay_buffer* buffer, const overlay_view* view);
void TDFX_StopOverlay(void);
status_t TDFX_Init(void);
bool TDFX_GetColorSpaceParams(int colorSpace, uint8& bpp);
bool TDFX_GetEdidInfo(edid1_info& edidInfo);
void TDFX_EngineReset(void);
void TDFX_EngineInit(const DisplayModeEx& mode);
bool TDFX_LoadCursorImage(int width, int height, uint8* and_mask, uint8* xor_mask);
void TDFX_SetCursorPosition(int x, int y);
void TDFX_AdjustFrame(const DisplayModeEx& mode);
status_t TDFX_SetDisplayMode(const DisplayModeEx& mode);
void TDFX_WaitForFifo(uint32);
void TDFX_WaitForIdle();
// Address of various VGA registers.
#define MISC_OUT_R 0x3cc // read
#define MISC_OUT_W 0x3c2 // write
#define CRTC_INDEX 0x3d4
#define CRTC_DATA 0x3d5
#define SEQ_INDEX 0x3c4
#define SEQ_DATA 0x3c5
// Macros for memory mapped I/O.
//===============================
#define INREG8(addr) *((vuint8*)(gInfo.regs + addr))
#define INREG16(addr) *((vuint16*)(gInfo.regs + addr))
#define INREG32(addr) *((vuint32*)(gInfo.regs + addr))
#define OUTREG8(addr, val) *((vuint8*)(gInfo.regs + addr)) = val
#define OUTREG16(addr, val) *((vuint16*)(gInfo.regs + addr)) = val
#define OUTREG32(addr, val) *((vuint32*)(gInfo.regs + addr)) = val
// Write a value to an 32-bit reg using a mask. The mask selects the
// bits to be modified.
#define OUTREGM(addr, value, mask) \
(OUTREG(addr, (INREG(addr) & ~mask) | (value & mask)))
#endif // _ACCELERANT_H

View File

@ -0,0 +1,81 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
Other authors:
Gerald Zajac 2007-2010
*/
#include "accelerant.h"
status_t
SetCursorShape(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.
SharedInfo& si = *gInfo.sharedInfo;
si.cursorHotX = hot_x;
si.cursorHotY = hot_y;
if ( ! TDFX_LoadCursorImage(width, height, andMask, xorMask))
return B_ERROR;
}
return B_OK;
}
void
MoveCursor(uint16 xPos, uint16 yPos)
{
// 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.
int x = xPos; // use signed int's since chip specific functions
int y = yPos; // need signed int to determine if cursor off screen
SharedInfo& si = *gInfo.sharedInfo;
DisplayModeEx& dm = si.displayMode;
uint16 hds = dm.h_display_start; // current horizontal starting pixel
uint16 vds = dm.v_display_start; // current vertical starting line
// Clamp cursor to virtual display.
if (x >= dm.virtual_width)
x = dm.virtual_width - 1;
if (y >= dm.virtual_height)
y = dm.virtual_height - 1;
// Adjust h/v display start to move cursor onto screen.
if (x >= (dm.timing.h_display + hds))
hds = x - dm.timing.h_display + 1;
else if (x < hds)
hds = x;
if (y >= (dm.timing.v_display + vds))
vds = y - dm.timing.v_display + 1;
else if (y < vds)
vds = y;
// Reposition the desktop on the display if required.
if (hds != dm.h_display_start || vds != dm.v_display_start)
MoveDisplay(hds, vds);
// Put cursor in correct physical position.
x -= (hds + si.cursorHotX);
y -= (vds + si.cursorHotY);
// Position the cursor on the display.
TDFX_SetCursorPosition(x, y);
}

View File

@ -0,0 +1,78 @@
/*
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
Other authors:
Gerald Zajac 2007-2010
*/
#include "accelerant.h"
static engine_token engineToken = { 1, B_2D_ACCELERATION, NULL };
uint32
AccelerantEngineCount(void)
{
return 1;
}
status_t
AcquireEngine(uint32 capabilities, uint32 max_wait,
sync_token* st, engine_token** et)
{
(void)capabilities; // avoid compiler warning for unused arg
(void)max_wait; // avoid compiler warning for unused arg
if (gInfo.sharedInfo->engineLock.Acquire() != B_OK)
return B_ERROR;
// Sync if required.
if (st)
SyncToToken(st);
// Return an engine token.
*et = &engineToken;
return B_OK;
}
status_t
ReleaseEngine(engine_token* et, sync_token* st)
{
// Update the sync token, if any.
if (st)
GetSyncToken(et, st);
gInfo.sharedInfo->engineLock.Release();
return B_OK;
}
void
WaitEngineIdle(void)
{
TDFX_WaitForIdle(); // wait until engine is completely idle
}
status_t
GetSyncToken(engine_token* et, sync_token* st)
{
st->engine_id = et->engine_id;
st->counter = 0;
return B_OK;
}
status_t
SyncToToken(sync_token* st)
{
(void)st; // avoid compiler warning for unused arg
WaitEngineIdle();
return B_OK;
}

View File

@ -0,0 +1,124 @@
/*
* Copyright 2008-2010 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
#include "accelerant.h"
extern "C" void*
get_accelerant_hook(uint32 feature, void* data)
{
(void)data; // avoid compiler warning for unused arg
switch (feature) {
// General
case B_INIT_ACCELERANT:
return (void*)InitAccelerant;
case B_UNINIT_ACCELERANT:
return (void*)UninitAccelerant;
case B_CLONE_ACCELERANT:
return (void*)CloneAccelerant;
case B_ACCELERANT_CLONE_INFO_SIZE:
return (void*)AccelerantCloneInfoSize;
case B_GET_ACCELERANT_CLONE_INFO:
return (void*)GetAccelerantCloneInfo;
case B_GET_ACCELERANT_DEVICE_INFO:
return (void*)GetAccelerantDeviceInfo;
case B_ACCELERANT_RETRACE_SEMAPHORE:
return NULL;
// Mode Configuration
case B_ACCELERANT_MODE_COUNT:
return (void*)AccelerantModeCount;
case B_GET_MODE_LIST:
return (void*)GetModeList;
case B_PROPOSE_DISPLAY_MODE:
return (void*)ProposeDisplayMode;
case B_SET_DISPLAY_MODE:
return (void*)SetDisplayMode;
case B_GET_DISPLAY_MODE:
return (void*)GetDisplayMode;
#ifdef __HAIKU__
case B_GET_PREFERRED_DISPLAY_MODE:
return NULL;
case B_GET_EDID_INFO:
return (void*)GetEdidInfo;
#endif
case B_GET_FRAME_BUFFER_CONFIG:
return (void*)GetFrameBufferConfig;
case B_GET_PIXEL_CLOCK_LIMITS:
return (void*)GetPixelClockLimits;
case B_MOVE_DISPLAY:
return (void*)MoveDisplay;
case B_SET_INDEXED_COLORS:
return (void*)(TDFX_SetIndexedColors);
case B_GET_TIMING_CONSTRAINTS:
return NULL;
// DPMS
case B_DPMS_CAPABILITIES:
return (void*)(TDFX_DPMSCapabilities);
case B_DPMS_MODE:
return (void*)(TDFX_GetDPMSMode);
case B_SET_DPMS_MODE:
return (void*)(TDFX_SetDPMSMode);
// Cursor
case B_SET_CURSOR_SHAPE:
return (void*)SetCursorShape;
case B_MOVE_CURSOR:
return (void*)MoveCursor;
case B_SHOW_CURSOR:
return (void*)(TDFX_ShowCursor);
// Engine Management
case B_ACCELERANT_ENGINE_COUNT:
return (void*)AccelerantEngineCount;
case B_ACQUIRE_ENGINE:
return (void*)AcquireEngine;
case B_RELEASE_ENGINE:
return (void*)ReleaseEngine;
case B_WAIT_ENGINE_IDLE:
return (void*)WaitEngineIdle;
case B_GET_SYNC_TOKEN:
return (void*)GetSyncToken;
case B_SYNC_TO_TOKEN:
return (void*)SyncToToken;
// 2D acceleration
case B_SCREEN_TO_SCREEN_BLIT:
return (void*)(TDFX_ScreenToScreenBlit);
case B_FILL_RECTANGLE:
return (void*)(TDFX_FillRectangle);
case B_INVERT_RECTANGLE:
return (void*)(TDFX_InvertRectangle);
case B_FILL_SPAN:
return (void*)(TDFX_FillSpan);
// Overlay
case B_OVERLAY_COUNT:
return (void*)OverlayCount;
case B_OVERLAY_SUPPORTED_SPACES:
return (void*)OverlaySupportedSpaces;
case B_OVERLAY_SUPPORTED_FEATURES:
return (void*)OverlaySupportedFeatures;
case B_ALLOCATE_OVERLAY_BUFFER:
return (void*)AllocateOverlayBuffer;
case B_RELEASE_OVERLAY_BUFFER:
return (void*)ReleaseOverlayBuffer;
case B_GET_OVERLAY_CONSTRAINTS:
return (void*)GetOverlayConstraints;
case B_ALLOCATE_OVERLAY:
return (void*)AllocateOverlay;
case B_RELEASE_OVERLAY:
return (void*)ReleaseOverlay;
case B_CONFIGURE_OVERLAY:
return (void*)ConfigureOverlay;
}
return NULL; // Return null pointer for any feature not handled above
}

View File

@ -0,0 +1,343 @@
/*
* Copyright 2007-2010 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
* Authors:
* Gerald Zajac
*/
#include "accelerant.h"
#include <create_display_modes.h> // common accelerant header file
#include <string.h>
#include <unistd.h>
static bool
IsThereEnoughFBMemory(const display_mode* mode, uint32 bitsPerPixel)
{
// Test if there is enough Frame Buffer memory for the mode and color depth
// specified by the caller, and return true if there is sufficient memory.
uint32 maxWidth = mode->virtual_width;
if (mode->timing.h_display > maxWidth)
maxWidth = mode->timing.h_display;
uint32 maxHeight = mode->virtual_height;
if (mode->timing.v_display > maxHeight)
maxHeight = mode->timing.v_display;
uint32 bytesPerPixel = (bitsPerPixel + 7) / 8;
return (maxWidth * maxHeight * bytesPerPixel < gInfo.sharedInfo->maxFrameBufferSize);
}
bool
IsModeUsable(const display_mode* mode)
{
// Test if the display mode is usable by the current video chip. That is,
// does the chip have enough memory for the mode and is the pixel clock
// within the chips allowable range, etc.
//
// Return true if the mode is usable.
SharedInfo& si = *gInfo.sharedInfo;
uint8 bitsPerPixel;
if (!TDFX_GetColorSpaceParams(mode->space, bitsPerPixel))
return false;
// Is there enough frame buffer memory to handle the mode?
if (!IsThereEnoughFBMemory(mode, bitsPerPixel))
return false;
if (mode->timing.pixel_clock > si.maxPixelClock)
return false;
// Is the color space supported?
bool colorSpaceSupported = false;
for (uint32 j = 0; j < si.colorSpaceCount; j++) {
if (mode->space == uint32(si.colorSpaces[j])) {
colorSpaceSupported = true;
break;
}
}
if (!colorSpaceSupported)
return false;
// In clock doubled mode for 3DFX chips, widths must be divisible by 16
// instead of 8.
if ((mode->timing.pixel_clock > si.maxPixelClock / 2)
&& (mode->timing.h_display % 16) != 0)
return false;
// Reject modes with a width of 640 and a height < 480 since they do not
// work properly with the 3DFX chipsets.
if (mode->timing.h_display == 640 && mode->timing.v_display < 480)
return false;
return true;
}
status_t
CreateModeList(bool (*checkMode)(const display_mode* mode))
{
SharedInfo& si = *gInfo.sharedInfo;
// Obtain EDID info which is needed for for building the mode list. If
// function TDFX_GetEdidInfo() fails, no attempt is made to read the EDID
// info from the video BIOS since that always failed when attempted during
// the development of this driver.
si.bHaveEDID = TDFX_GetEdidInfo(si.edidInfo);
if (si.bHaveEDID) {
#ifdef ENABLE_DEBUG_TRACE
edid_dump(&(si.edidInfo));
#endif
} else {
TRACE("CreateModeList(); Unable to get EDID info\n");
}
display_mode* list;
uint32 count = 0;
area_id listArea;
listArea = create_display_modes("3DFX modes",
si.bHaveEDID ? &si.edidInfo : NULL,
NULL, 0, si.colorSpaces, si.colorSpaceCount,
(check_display_mode_hook)checkMode, &list, &count);
if (listArea < 0)
return listArea; // listArea has error code
si.modeArea = gInfo.modeListArea = listArea;
si.modeCount = count;
gInfo.modeList = list;
return B_OK;
}
status_t
ProposeDisplayMode(display_mode *target, const display_mode *low,
const display_mode *high)
{
(void)low; // avoid compiler warning for unused arg
(void)high; // avoid compiler warning for unused arg
SharedInfo& si = *gInfo.sharedInfo;
TRACE("ProposeDisplayMode() %dx%d, pixel clock: %d kHz, space: 0x%X\n",
target->timing.h_display, target->timing.v_display,
target->timing.pixel_clock, target->space);
// In clock doubled mode for 3DFX chips, widths must be divisible by 16
// instead of 8.
if ((target->timing.pixel_clock > si.maxPixelClock / 2)
&& (target->timing.h_display % 16) != 0)
return B_BAD_VALUE;
// Search the mode list for the specified mode.
uint32 modeCount = si.modeCount;
for (uint32 j = 0; j < modeCount; j++) {
display_mode& mode = gInfo.modeList[j];
if (target->timing.h_display == mode.timing.h_display
&& target->timing.v_display == mode.timing.v_display
&& target->space == mode.space)
return B_OK; // mode found in list
}
return B_BAD_VALUE; // mode not found in list
}
status_t
SetDisplayMode(display_mode* pMode)
{
// First validate the mode, then call a function to set the registers.
TRACE("SetDisplayMode() begin\n");
SharedInfo& si = *gInfo.sharedInfo;
DisplayModeEx mode;
(display_mode&)mode = *pMode;
if (!TDFX_GetColorSpaceParams(mode.space, mode.bitsPerPixel))
return B_BAD_VALUE;
if (ProposeDisplayMode(&mode, pMode, pMode) != B_OK)
return B_BAD_VALUE;
mode.bytesPerPixel = (mode.bitsPerPixel + 7) / 8;
mode.bytesPerRow = mode.timing.h_display * mode.bytesPerPixel;
// Is there enough frame buffer memory for this mode?
if ( ! IsThereEnoughFBMemory(&mode, mode.bitsPerPixel))
return B_NO_MEMORY;
TRACE("Set display mode: %dx%d virtual size: %dx%d color depth: %d bits/pixel\n",
mode.timing.h_display, mode.timing.v_display,
mode.virtual_width, mode.virtual_height, mode.bitsPerPixel);
TRACE(" mode timing: %d %d %d %d %d %d %d %d %d\n",
mode.timing.pixel_clock,
mode.timing.h_display,
mode.timing.h_sync_start, mode.timing.h_sync_end,
mode.timing.h_total,
mode.timing.v_display,
mode.timing.v_sync_start, mode.timing.v_sync_end,
mode.timing.v_total);
TRACE(" mode hFreq: %.1f kHz vFreq: %.1f Hz %chSync %cvSync\n",
double(mode.timing.pixel_clock) / mode.timing.h_total,
((double(mode.timing.pixel_clock) / mode.timing.h_total) * 1000.0)
/ mode.timing.v_total,
(mode.timing.flags & B_POSITIVE_HSYNC) ? '+' : '-',
(mode.timing.flags & B_POSITIVE_VSYNC) ? '+' : '-');
status_t status = TDFX_SetDisplayMode(mode);
if (status != B_OK) {
TRACE("SetDisplayMode() failed; status 0x%x\n", status);
return status;
}
si.displayMode = mode;
TRACE("SetDisplayMode() done\n");
return B_OK;
}
status_t
MoveDisplay(uint16 horizontalStart, uint16 verticalStart)
{
// 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.
DisplayModeEx& mode = gInfo.sharedInfo->displayMode;
if (mode.timing.h_display + horizontalStart > mode.virtual_width
|| mode.timing.v_display + verticalStart > mode.virtual_height)
return B_ERROR;
mode.h_display_start = horizontalStart;
mode.v_display_start = verticalStart;
TDFX_AdjustFrame(mode);
return B_OK;
}
uint32
AccelerantModeCount(void)
{
// Return the number of display modes in the mode list.
return gInfo.sharedInfo->modeCount;
}
status_t
GetModeList(display_mode* dmList)
{
// Copy the list of supported video modes to the location pointed at
// by dmList.
memcpy(dmList, gInfo.modeList, gInfo.sharedInfo->modeCount * sizeof(display_mode));
return B_OK;
}
status_t
GetDisplayMode(display_mode* current_mode)
{
*current_mode = gInfo.sharedInfo->displayMode; // return current display mode
return B_OK;
}
status_t
GetFrameBufferConfig(frame_buffer_config* pFBC)
{
SharedInfo& si = *gInfo.sharedInfo;
pFBC->frame_buffer = (void*)((addr_t)si.videoMemAddr + si.frameBufferOffset);
pFBC->frame_buffer_dma = (void*)((addr_t)si.videoMemPCI + si.frameBufferOffset);
pFBC->bytes_per_row = si.displayMode.virtual_width * si.displayMode.bytesPerPixel;
return B_OK;
}
status_t
GetPixelClockLimits(display_mode* mode, uint32* low, uint32* high)
{
// Return the maximum and minium pixel clock limits for the specified mode.
SharedInfo& si = *gInfo.sharedInfo;
uint8 bitsPerPixel;
if (!TDFX_GetColorSpaceParams(mode->space, bitsPerPixel))
return B_ERROR;
if (low != NULL) {
// lower limit of about 48Hz vertical refresh
uint32 totalClocks = (uint32)mode->timing.h_total
* (uint32)mode->timing.v_total;
uint32 lowClock = (totalClocks * 48L) / 1000L;
if (lowClock > si.maxPixelClock)
return B_ERROR;
*low = lowClock;
}
if (high != NULL) {
// In clock doubled mode for 3DFX chips, widths must be divisible by 16
// instead of 8. If width is not divisible by 16, limit pixel clock to
// half of max pixel clock since divisor is 8 if pixel clock is < half
// max pixel clock.
if ((mode->timing.h_display % 16) != 0)
*high = si.maxPixelClock / 2;
else
*high = si.maxPixelClock;
}
return B_OK;
}
#ifdef __HAIKU__
status_t
GetEdidInfo(void* info, size_t size, uint32* _version)
{
SharedInfo& si = *gInfo.sharedInfo;
if ( ! si.bHaveEDID)
return B_ERROR;
if (size < sizeof(struct edid1_info))
return B_BUFFER_OVERFLOW;
memcpy(info, &si.edidInfo, sizeof(struct edid1_info));
*_version = EDID_VERSION_1;
return B_OK;
}
#endif // __HAIKU__

View File

@ -0,0 +1,318 @@
/*
* Copyright 2010 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
#include "accelerant.h"
#include "3dfx.h"
#include <stdlib.h>
uint32
OverlayCount(const display_mode *mode)
{
(void)mode; // avoid compiler warning for unused arg
return 1;
}
const uint32*
OverlaySupportedSpaces(const display_mode* mode)
{
(void)mode; // avoid compiler warning for unused arg
static const uint32 kSupportedSpaces[] = {B_RGB16, B_YCbCr422, 0};
return kSupportedSpaces;
}
uint32
OverlaySupportedFeatures(uint32 colorSpace)
{
(void)colorSpace; // avoid compiler warning for unused arg
return B_OVERLAY_COLOR_KEY
| B_OVERLAY_HORIZONTAL_FILTERING
| B_OVERLAY_VERTICAL_FILTERING;
}
const overlay_buffer*
AllocateOverlayBuffer(color_space colorSpace, uint16 width, uint16 height)
{
SharedInfo& si = *gInfo.sharedInfo;
TRACE("AllocateOverlayBuffer() width %u, height %u, colorSpace 0x%lx\n",
width, height, colorSpace);
si.overlayLock.Acquire();
// Note: When allocating buffers, buffer allocation starts at the end of
// video memory, and works backward to the end of the video frame buffer.
// The allocated buffers are recorded in a linked list of OverlayBuffer
// objects which are ordered by the buffer address with the first object
// in the list having the highest buffer address.
uint32 bytesPerPixel;
switch (colorSpace) {
case B_YCbCr422:
case B_RGB16:
bytesPerPixel = 2;
break;
default:
si.overlayLock.Release();
TRACE("AllocateOverlayBuffer() unsupported color space 0x%x\n",
colorSpace);
return NULL;
}
// Calculate required buffer size as a multiple of 1K.
uint32 buffSize = (width * bytesPerPixel * height + 0x3ff) & ~0x3ff;
// If the buffer area starts at the end of the video memory, the Voodoo5
// chip has the following display problems: displays a pink/red band at
// the bottom of the overlay display, leaves some artifacts at the top of
// screen, and messes up the displayed cursor when a hardware cursor is
// used (cursor image is at beginning of video memory). I don't know
// whether the Voodoo5 goes beyond the buffer area or whether this is some
// sort of memory mapping problem; nevertheless, adding 4k or 8K to the
// buffer size solves the problem. Thus, 16K is added to the buffer size.
if (si.chipType == VOODOO_5)
buffSize += 16 * 1024;
OverlayBuffer* ovBuff = si.overlayBuffer;
OverlayBuffer* prevOvBuff = NULL;
// If no buffers have been allocated, prevBuffAddr calculated here will be
// the address where the buffer area will start.
uint32 prevBuffAddr = si.videoMemAddr + si.frameBufferOffset
+ si.maxFrameBufferSize;
while (ovBuff != NULL) {
// Test if there is sufficient space between the end of the current
// buffer and the start of the previous buffer to allocate the new
// buffer.
uint32 currentBuffEndAddr = (addr_t)ovBuff->buffer + ovBuff->size;
if ((prevBuffAddr - currentBuffEndAddr) >= buffSize)
break; // sufficient space for the new buffer
prevBuffAddr = (uint32)ovBuff->buffer;
prevOvBuff = ovBuff;
ovBuff = ovBuff->nextBuffer;
}
OverlayBuffer* nextOvBuff = ovBuff;
if (ovBuff == NULL) {
// No space between any current buffers of the required size was found;
// thus space must be allocated between the last buffer and the end of
// the video frame buffer. Compute where current video frame buffer
// ends so that it can be determined if there is sufficient space for
// the new buffer to be created.
uint32 fbEndAddr = si.videoMemAddr + si.frameBufferOffset
+ (si.displayMode.virtual_width * si.displayMode.bytesPerPixel
* si.displayMode.virtual_height);
if (buffSize > prevBuffAddr - fbEndAddr) {
si.overlayLock.Release();
TRACE("AllocateOverlayBuffer() insuffcient space for %ld (0x%lx) "
"byte buffer\n", buffSize, buffSize);
return NULL; // insufficient space for buffer
}
nextOvBuff = NULL;
}
ovBuff = (OverlayBuffer*)malloc(sizeof(OverlayBuffer));
if (ovBuff == NULL) {
si.overlayLock.Release();
return NULL; // memory not available for OverlayBuffer struct
}
ovBuff->nextBuffer = nextOvBuff;
ovBuff->size = buffSize;
ovBuff->space = colorSpace;
ovBuff->width = width;
ovBuff->height = height;
ovBuff->bytes_per_row = width * bytesPerPixel;
ovBuff->buffer = (void*)(prevBuffAddr - buffSize);
ovBuff->buffer_dma = (void*)(si.videoMemPCI
+ ((addr_t)ovBuff->buffer - si.videoMemAddr));
if (prevOvBuff == NULL)
si.overlayBuffer = ovBuff;
else
prevOvBuff->nextBuffer = ovBuff;
si.overlayLock.Release();
TRACE("AllocateOverlayBuffer() allocated %ld (0x%lx) byte buffer at 0x%lx\n",
buffSize, buffSize, ovBuff->buffer);
return ovBuff;
}
status_t
ReleaseOverlayBuffer(const overlay_buffer* buffer)
{
SharedInfo& si = *gInfo.sharedInfo;
TRACE("ReleaseOverlayBuffer() called\n");
if (buffer == NULL)
return B_BAD_VALUE;
// Find the buffer to be released.
OverlayBuffer* ovBuff = si.overlayBuffer;
OverlayBuffer* prevOvBuff = NULL;
while (ovBuff != NULL) {
if (ovBuff->buffer == buffer->buffer) {
// Buffer to be released has been found. Remove the OverlayBuffer
// object from the chain of overlay buffers.
if (prevOvBuff == NULL)
si.overlayBuffer = ovBuff->nextBuffer;
else
prevOvBuff->nextBuffer = ovBuff->nextBuffer;
free(ovBuff);
TRACE("ReleaseOverlayBuffer() return OK\n");
return B_OK;
}
prevOvBuff = ovBuff;
ovBuff = ovBuff->nextBuffer;
}
return B_ERROR; // buffer to be released not found in chain of buffers
}
status_t
GetOverlayConstraints(const display_mode* mode, const overlay_buffer* buffer,
overlay_constraints *constraints)
{
if ((mode == NULL) || (buffer == NULL) || (constraints == NULL))
return B_ERROR;
// Position (values are in pixels)
constraints->view.h_alignment = 0;
constraints->view.v_alignment = 0;
switch (buffer->space) {
case B_YCbCr422:
case B_RGB16:
constraints->view.width_alignment = 7;
break;
default:
TRACE("GetOverlayConstraints() color space 0x%x out of range\n",
buffer->space);
return B_BAD_VALUE;
}
constraints->view.height_alignment = 0;
//Size
constraints->view.width.min = 4;
constraints->view.height.min = 4;
constraints->view.width.max = buffer->width;
constraints->view.height.max = buffer->height;
// Scaler output restrictions
constraints->window.h_alignment = 0;
constraints->window.v_alignment = 0;
constraints->window.width_alignment = 0;
constraints->window.height_alignment = 0;
constraints->window.width.min = 2;
constraints->window.width.max = mode->virtual_width;
constraints->window.height.min = 2;
constraints->window.height.max = mode->virtual_height;
constraints->h_scale.min = 1.0;
constraints->h_scale.max = 8.0;
constraints->v_scale.min = 1.0;
constraints->v_scale.max = 8.0;
return B_OK;
}
overlay_token
AllocateOverlay(void)
{
SharedInfo& si = *gInfo.sharedInfo;
TRACE("AllocateOverlay() called\n");
// There is only a single overlay channel; thus, check if it is already
// allocated.
if( atomic_or(&si.overlayAllocated, 1) != 0) {
TRACE("AllocateOverlay() overlay channel already in use\n");
return NULL;
}
TRACE("AllocateOverlay() Overlay allocated, overlayToken: %d\n",
si.overlayToken);
return (overlay_token)++si.overlayToken;
}
status_t
ReleaseOverlay(overlay_token overlayToken)
{
SharedInfo& si = *gInfo.sharedInfo;
TRACE("ReleaseOverlay() called\n");
if (overlayToken != (overlay_token)si.overlayToken)
return B_BAD_VALUE;
TDFX_StopOverlay();
atomic_and(&si.overlayAllocated, 0); // mark overlay as unallocated
TRACE("ReleaseOverlay() return OK\n");
return B_OK;
}
status_t
ConfigureOverlay (overlay_token overlayToken, const overlay_buffer* buffer,
const overlay_window* window, const overlay_view* view)
{
SharedInfo& si = *gInfo.sharedInfo;
if (overlayToken != (overlay_token)si.overlayToken)
return B_BAD_VALUE;
if (buffer == NULL)
return B_BAD_VALUE;
if (window == NULL || view == NULL) {
TDFX_StopOverlay();
TRACE("ConfigureOverlay() hide only\n");
return B_OK;
}
// Program the overlay hardware.
if (!TDFX_DisplayOverlay(window, buffer, view)) {
TRACE("ConfigureOverlay(), call to TDFX_DisplayOverlay() returned error\n");
return B_ERROR;
}
return B_OK;
}

View File

@ -1,5 +1,6 @@
SubDir HAIKU_TOP src add-ons accelerants ;
SubInclude HAIKU_TOP src add-ons accelerants 3dfx ;
SubInclude HAIKU_TOP src add-ons accelerants ati ;
SubInclude HAIKU_TOP src add-ons accelerants common ;
SubInclude HAIKU_TOP src add-ons accelerants et6x00 ;

View File

@ -0,0 +1,9 @@
SubDir HAIKU_TOP src add-ons kernel drivers graphics 3dfx ;
UsePrivateHeaders [ FDirName graphics 3dfx ] ;
UsePrivateHeaders [ FDirName graphics common ] ;
UsePrivateHeaders graphics kernel ;
KernelAddon 3dfx :
driver.cpp
;

View File

@ -0,0 +1,563 @@
/*
* Copyright 2007-2010 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
#include <KernelExport.h>
#include <PCI.h>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <graphic_driver.h>
#ifdef __HAIKU__
#include <boot_item.h>
#include <arch/x86/vm86.h>
#endif // __HAIKU__
#include "DriverInterface.h"
#undef TRACE
#ifdef ENABLE_DEBUG_TRACE
# define TRACE(x...) dprintf("3dfx: " x)
#else
# define TRACE(x...) ;
#endif
#define ACCELERANT_NAME "3dfx.accelerant"
#define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1))
#define SKD_HANDLER_INSTALLED 0x80000000
#define MAX_DEVICES 4
#define DEVICE_FORMAT "%04X_%04X_%02X%02X%02X"
int32 api_version = B_CUR_DRIVER_API_VERSION; // revision of driver API we support
#define VENDOR_ID 0x121A // 3DFX vendor ID
struct ChipInfo {
uint16 chipID; // PCI device id of the chip
ChipType chipType; // assigned chip type identifier
const char* chipName; // user recognizable name for chip (must be < 32 chars)
};
// This table maps a PCI device ID to a chip type identifier and the chip name.
static const ChipInfo chipTable[] = {
{ 0x03, BANSHEE, "Banshee" },
{ 0x05, VOODOO_3, "Voodoo 3" },
{ 0x09, VOODOO_5, "Voodoo 5" },
{ 0, TDFX_NONE, NULL }
};
struct DeviceInfo {
uint32 openCount; // count of how many times device has been opened
int32 flags;
area_id sharedArea; // area shared between driver and all accelerants
SharedInfo* sharedInfo; // pointer to shared info area memory
vuint8* regs; // pointer to memory mapped registers
const ChipInfo* pChipInfo; // info about the selected chip
pci_info pciInfo; // copy of pci info for this device
char name[B_OS_NAME_LENGTH]; // name of device
};
static Benaphore gLock;
static DeviceInfo gDeviceInfo[MAX_DEVICES];
static char* gDeviceNames[MAX_DEVICES + 1];
static pci_module_info* gPCI;
// Prototypes for device hook functions.
static status_t device_open(const char* name, uint32 flags, void** cookie);
static status_t device_close(void* dev);
static status_t device_free(void* dev);
static status_t device_read(void* dev, off_t pos, void* buf, size_t* len);
static status_t device_write(void* dev, off_t pos, const void* buf, size_t* len);
static status_t device_ioctl(void* dev, uint32 msg, void* buf, size_t len);
static device_hooks gDeviceHooks =
{
device_open,
device_close,
device_free,
device_ioctl,
device_read,
device_write,
NULL,
NULL,
NULL,
NULL
};
static inline uint32
GetPCI(pci_info& info, uint8 offset, uint8 size)
{
return gPCI->read_pci_config(info.bus, info.device, info.function, offset, size);
}
static inline void
SetPCI(pci_info& info, uint8 offset, uint8 size, uint32 value)
{
gPCI->write_pci_config(info.bus, info.device, info.function, offset, size, value);
}
static status_t
MapDevice(DeviceInfo& di)
{
SharedInfo& si = *(di.sharedInfo);
pci_info& pciInfo = di.pciInfo;
TRACE("enter MapDevice()\n");
// Enable memory mapped IO and bus master.
SetPCI(pciInfo, PCI_command, 2, GetPCI(pciInfo, PCI_command, 2)
| PCI_command_io | PCI_command_memory | PCI_command_master);
// Map the video memory.
phys_addr_t videoRamAddr = pciInfo.u.h0.base_registers[1];
uint32 videoRamSize = pciInfo.u.h0.base_register_sizes[1];
si.videoMemPCI = videoRamAddr;
char frameBufferAreaName[] = "3DFX frame buffer";
si.videoMemArea = map_physical_memory(
frameBufferAreaName,
videoRamAddr,
videoRamSize,
B_ANY_KERNEL_BLOCK_ADDRESS | B_MTR_WC,
B_READ_AREA + B_WRITE_AREA,
(void**)&si.videoMemAddr);
TRACE("Video memory, area: %ld, addr: 0x%lX, size: %ld\n", si.videoMemArea, (uint32)(si.videoMemAddr), videoRamSize);
if (si.videoMemArea < 0) {
// Try to map this time without write combining.
si.videoMemArea = map_physical_memory(
frameBufferAreaName,
videoRamAddr,
videoRamSize,
B_ANY_KERNEL_BLOCK_ADDRESS,
B_READ_AREA + B_WRITE_AREA,
(void**)&si.videoMemAddr);
}
if (si.videoMemArea < 0)
return si.videoMemArea;
// Map the MMIO register area.
phys_addr_t regsBase = pciInfo.u.h0.base_registers[0];
uint32 regAreaSize = pciInfo.u.h0.base_register_sizes[0];
si.regsArea = map_physical_memory("3DFX mmio registers",
regsBase,
regAreaSize,
B_ANY_KERNEL_ADDRESS,
0, // neither read nor write, to hide it from user space apps
(void**)&di.regs);
// If there was an error, delete other areas.
if (si.regsArea < 0) {
delete_area(si.videoMemArea);
si.videoMemArea = -1;
}
TRACE("leave MapDevice(); result: %ld\n", si.regsArea);
return si.regsArea;
}
static void
UnmapDevice(DeviceInfo& di)
{
SharedInfo& si = *(di.sharedInfo);
if (si.regsArea >= 0)
delete_area(si.regsArea);
if (si.videoMemArea >= 0)
delete_area(si.videoMemArea);
si.regsArea = si.videoMemArea = -1;
si.videoMemAddr = (addr_t)NULL;
di.regs = NULL;
}
static status_t
InitDevice(DeviceInfo& di)
{
// Perform initialization and mapping of the device, and return B_OK if
// sucessful; else, return error code.
// Create the area for shared info with NO user-space read or write
// permissions, to prevent accidental damage.
TRACE("enter InitDevice()\n");
size_t sharedSize = (sizeof(SharedInfo) + 7) & ~7;
di.sharedArea = create_area("3DFX shared info",
(void**) &(di.sharedInfo),
B_ANY_KERNEL_ADDRESS,
ROUND_TO_PAGE_SIZE(sharedSize),
B_FULL_LOCK, 0);
if (di.sharedArea < 0)
return di.sharedArea; // return error code
SharedInfo& si = *(di.sharedInfo);
memset(&si, 0, sharedSize);
pci_info& pciInfo = di.pciInfo;
si.vendorID = pciInfo.vendor_id;
si.deviceID = pciInfo.device_id;
si.revision = pciInfo.revision;
si.chipType = di.pChipInfo->chipType;
strcpy(si.chipName, di.pChipInfo->chipName);
status_t status = MapDevice(di);
if (status < 0) {
delete_area(di.sharedArea);
di.sharedArea = -1;
di.sharedInfo = NULL;
return status; // return error code
}
return B_OK;
}
static const ChipInfo*
GetNextSupportedDevice(uint32& pciIndex, pci_info& pciInfo)
{
// Search the PCI devices for a device that is supported by this driver.
// The search starts at the device specified by argument pciIndex, and
// continues until a supported device is found or there are no more devices
// to examine. Argument pciIndex is incremented after each device is
// examined.
// If a supported device is found, return a pointer to the struct containing
// the chip info; else return NULL.
while (gPCI->get_nth_pci_info(pciIndex, &pciInfo) == B_OK) {
if (pciInfo.vendor_id == VENDOR_ID) {
// Search the table of supported devices to find a chip/device that
// matches device ID of the current PCI device.
const ChipInfo* pDevice = chipTable;
while (pDevice->chipID != 0) { // end of table?
if (pDevice->chipID == pciInfo.device_id)
return pDevice; // matching device/chip found
pDevice++;
}
}
pciIndex++;
}
return NULL; // no supported device found
}
// #pragma mark - Kernel Interface
status_t
init_hardware(void)
{
// Return B_OK if a device supported by this driver is found; otherwise,
// return B_ERROR so the driver will be unloaded.
if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI) != B_OK)
return B_ERROR; // unable to access PCI bus
// Check pci devices for a device supported by this driver.
uint32 pciIndex = 0;
pci_info pciInfo;
const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, pciInfo);
TRACE("init_hardware() - %s\n", pDevice == NULL ? "no supported devices" : "device supported");
put_module(B_PCI_MODULE_NAME); // put away the module manager
return (pDevice == NULL ? B_ERROR : B_OK);
}
status_t init_driver(void)
{
// Get handle for the pci bus.
if (get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI) != B_OK)
return B_ERROR;
status_t status = gLock.Init("3DFX driver lock");
if (status < B_OK)
return status;
// Get info about all the devices supported by this driver.
uint32 pciIndex = 0;
uint32 count = 0;
while (count < MAX_DEVICES) {
DeviceInfo& di = gDeviceInfo[count];
const ChipInfo* pDevice = GetNextSupportedDevice(pciIndex, di.pciInfo);
if (pDevice == NULL)
break; // all supported devices have been obtained
// Compose device name.
sprintf(di.name, "graphics/" DEVICE_FORMAT,
di.pciInfo.vendor_id, di.pciInfo.device_id,
di.pciInfo.bus, di.pciInfo.device, di.pciInfo.function);
TRACE("init_driver() match found; name: %s\n", di.name);
gDeviceNames[count] = di.name;
di.openCount = 0; // mark driver as available for R/W open
di.sharedArea = -1; // indicate shared area not yet created
di.sharedInfo = NULL;
di.pChipInfo = pDevice;
count++;
pciIndex++;
}
gDeviceNames[count] = NULL; // terminate list with null pointer
TRACE("init_driver() %ld supported devices\n", count);
return B_OK;
}
void
uninit_driver(void)
{
// Free the driver data.
gLock.Delete();
put_module(B_PCI_MODULE_NAME); // put the pci module away
}
const char**
publish_devices(void)
{
return (const char**)gDeviceNames; // return list of supported devices
}
device_hooks*
find_device(const char* name)
{
int i = 0;
while (gDeviceNames[i] != NULL) {
if (strcmp(name, gDeviceNames[i]) == 0)
return &gDeviceHooks;
i++;
}
return NULL;
}
// #pragma mark - Device Hooks
static status_t
device_open(const char* name, uint32 /*flags*/, void** cookie)
{
status_t status = B_OK;
TRACE("device_open() - name: %s, cookie: 0x%08lx)\n", name, (uint32)cookie);
// Find the device name in the list of devices.
int32 i = 0;
while (gDeviceNames[i] != NULL && (strcmp(name, gDeviceNames[i]) != 0))
i++;
if (gDeviceNames[i] == NULL)
return B_BAD_VALUE; // device name not found in list of devices
DeviceInfo& di = gDeviceInfo[i];
gLock.Acquire(); // make sure no one else has write access to common data
if (di.openCount == 0)
status = InitDevice(di);
gLock.Release();
if (status == B_OK) {
di.openCount++; // mark device open
*cookie = &di; // send cookie to opener
}
TRACE("device_open() returning 0x%lx, open count: %ld\n", status, di.openCount);
return status;
}
static status_t
device_read(void* dev, off_t pos, void* buf, size_t* len)
{
// Following 3 lines of code are here to eliminate "unused parameter" warnings.
(void)dev;
(void)pos;
(void)buf;
*len = 0;
return B_NOT_ALLOWED;
}
static status_t
device_write(void* dev, off_t pos, const void* buf, size_t* len)
{
// Following 3 lines of code are here to eliminate "unused parameter" warnings.
(void)dev;
(void)pos;
(void)buf;
*len = 0;
return B_NOT_ALLOWED;
}
static status_t
device_close(void* dev)
{
(void)dev; // avoid compiler warning for unused arg
TRACE("device_close()\n");
return B_NO_ERROR;
}
static status_t
device_free(void* dev)
{
DeviceInfo& di = *((DeviceInfo*)dev);
TRACE("enter device_free()\n");
gLock.Acquire(); // lock driver
// If opened multiple times, merely decrement the open count and exit.
if (di.openCount <= 1) {
UnmapDevice(di); // free regs and frame buffer areas
delete_area(di.sharedArea);
di.sharedArea = -1;
di.sharedInfo = NULL;
}
if (di.openCount > 0)
di.openCount--; // mark device available
gLock.Release(); // unlock driver
TRACE("exit device_free() openCount: %ld\n", di.openCount);
return B_OK;
}
static status_t
device_ioctl(void* dev, uint32 msg, void* buffer, size_t bufferLength)
{
DeviceInfo& di = *((DeviceInfo*)dev);
#ifndef __HAIKU__
(void)bufferLength; // avoid compiler warning for unused arg
#endif
switch (msg) {
case B_GET_ACCELERANT_SIGNATURE:
strcpy((char*)buffer, ACCELERANT_NAME);
return B_OK;
case TDFX_DEVICE_NAME:
strncpy((char*)buffer, di.name, B_OS_NAME_LENGTH);
((char*)buffer)[B_OS_NAME_LENGTH -1] = '\0';
return B_OK;
case TDFX_GET_SHARED_DATA:
#ifdef __HAIKU__
if (bufferLength != sizeof(area_id))
return B_BAD_DATA;
#endif
*((area_id*)buffer) = di.sharedArea;
return B_OK;
case TDFX_GET_PIO_REG:
{
#ifdef __HAIKU__
if (bufferLength != sizeof(PIORegInfo))
return B_BAD_DATA;
#endif
PIORegInfo* regInfo = (PIORegInfo*)buffer;
if (regInfo->magic == TDFX_PRIVATE_DATA_MAGIC) {
int ioAddr = di.pciInfo.u.h0.base_registers[2] + regInfo->offset;
if (regInfo->index >= 0) {
gPCI->write_io_8(ioAddr, regInfo->index);
regInfo->value = gPCI->read_io_8(ioAddr + 1);
} else {
regInfo->value = gPCI->read_io_8(ioAddr);
}
return B_OK;
}
break;
}
case TDFX_SET_PIO_REG:
{
#ifdef __HAIKU__
if (bufferLength != sizeof(PIORegInfo))
return B_BAD_DATA;
#endif
PIORegInfo* regInfo = (PIORegInfo*)buffer;
if (regInfo->magic == TDFX_PRIVATE_DATA_MAGIC) {
int ioAddr = di.pciInfo.u.h0.base_registers[2] + regInfo->offset;
if (regInfo->index >= 0) {
gPCI->write_io_8(ioAddr, regInfo->index);
gPCI->write_io_8(ioAddr + 1, regInfo->value);
} else {
gPCI->write_io_8(ioAddr, regInfo->value);
}
return B_OK;
}
break;
}
}
return B_DEV_INVALID_IOCTL;
}

View File

@ -1,5 +1,6 @@
SubDir HAIKU_TOP src add-ons kernel drivers graphics ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics 3dfx ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics ati ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics common ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics et6x00 ;
@ -11,8 +12,8 @@ SubInclude HAIKU_TOP src add-ons kernel drivers graphics nvidia_gpgpu ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics radeon ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics radeon_hd ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics s3 ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics tdfx ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics skeleton ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics tdfx ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics vesa ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics via ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics vmware ;