From a56bc48835ebabc25dfd5ec8a6a52e3cd5906bb1 Mon Sep 17 00:00:00 2001 From: Philippe Houdoin Date: Thu, 24 Jun 2010 03:47:49 +0000 Subject: [PATCH] =?UTF-8?q?Im=C3=83port=20new=203dfx=20graphics=20driver?= =?UTF-8?q?=20from=20Gerald=20Zajac=20(see=20#6215.=20Supports=20Banshee,?= =?UTF-8?q?=20Voodoo3=20and=20Voodoo5=20chips.=20It=20will=20be=20promoted?= =?UTF-8?q?=20as=20older=20tdfx=20replacement=20soon,=20but=20not=20until?= =?UTF-8?q?=20my=20small=20changes=20around=20phys=5Faddr=5Ft=20are=20vali?= =?UTF-8?q?dated.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37241 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../private/graphics/3dfx/DriverInterface.h | 153 +++++ src/add-ons/accelerants/3dfx/3dfx.h | 116 ++++ src/add-ons/accelerants/3dfx/3dfx_cursor.cpp | 83 +++ src/add-ons/accelerants/3dfx/3dfx_dpms.cpp | 87 +++ src/add-ons/accelerants/3dfx/3dfx_draw.cpp | 152 +++++ src/add-ons/accelerants/3dfx/3dfx_edid.cpp | 64 ++ src/add-ons/accelerants/3dfx/3dfx_init.cpp | 89 +++ src/add-ons/accelerants/3dfx/3dfx_mode.cpp | 402 +++++++++++++ src/add-ons/accelerants/3dfx/3dfx_overlay.cpp | 121 ++++ src/add-ons/accelerants/3dfx/Jamfile | 24 + src/add-ons/accelerants/3dfx/accelerant.cpp | 196 ++++++ src/add-ons/accelerants/3dfx/accelerant.h | 186 ++++++ src/add-ons/accelerants/3dfx/cursor.cpp | 81 +++ src/add-ons/accelerants/3dfx/engine.cpp | 78 +++ src/add-ons/accelerants/3dfx/hooks.cpp | 124 ++++ src/add-ons/accelerants/3dfx/mode.cpp | 343 +++++++++++ src/add-ons/accelerants/3dfx/overlay.cpp | 318 ++++++++++ src/add-ons/accelerants/Jamfile | 1 + .../kernel/drivers/graphics/3dfx/Jamfile | 9 + .../kernel/drivers/graphics/3dfx/driver.cpp | 563 ++++++++++++++++++ src/add-ons/kernel/drivers/graphics/Jamfile | 3 +- 21 files changed, 3192 insertions(+), 1 deletion(-) create mode 100644 headers/private/graphics/3dfx/DriverInterface.h create mode 100644 src/add-ons/accelerants/3dfx/3dfx.h create mode 100644 src/add-ons/accelerants/3dfx/3dfx_cursor.cpp create mode 100644 src/add-ons/accelerants/3dfx/3dfx_dpms.cpp create mode 100644 src/add-ons/accelerants/3dfx/3dfx_draw.cpp create mode 100644 src/add-ons/accelerants/3dfx/3dfx_edid.cpp create mode 100644 src/add-ons/accelerants/3dfx/3dfx_init.cpp create mode 100644 src/add-ons/accelerants/3dfx/3dfx_mode.cpp create mode 100644 src/add-ons/accelerants/3dfx/3dfx_overlay.cpp create mode 100644 src/add-ons/accelerants/3dfx/Jamfile create mode 100644 src/add-ons/accelerants/3dfx/accelerant.cpp create mode 100644 src/add-ons/accelerants/3dfx/accelerant.h create mode 100644 src/add-ons/accelerants/3dfx/cursor.cpp create mode 100644 src/add-ons/accelerants/3dfx/engine.cpp create mode 100644 src/add-ons/accelerants/3dfx/hooks.cpp create mode 100644 src/add-ons/accelerants/3dfx/mode.cpp create mode 100644 src/add-ons/accelerants/3dfx/overlay.cpp create mode 100644 src/add-ons/kernel/drivers/graphics/3dfx/Jamfile create mode 100644 src/add-ons/kernel/drivers/graphics/3dfx/driver.cpp diff --git a/headers/private/graphics/3dfx/DriverInterface.h b/headers/private/graphics/3dfx/DriverInterface.h new file mode 100644 index 0000000000..7136be7198 --- /dev/null +++ b/headers/private/graphics/3dfx/DriverInterface.h @@ -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 +#include +#include +#include +#include + + +// 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 diff --git a/src/add-ons/accelerants/3dfx/3dfx.h b/src/add-ons/accelerants/3dfx/3dfx.h new file mode 100644 index 0000000000..f3dffd7ede --- /dev/null +++ b/src/add-ons/accelerants/3dfx/3dfx.h @@ -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__ diff --git a/src/add-ons/accelerants/3dfx/3dfx_cursor.cpp b/src/add-ons/accelerants/3dfx/3dfx_cursor.cpp new file mode 100644 index 0000000000..c7c8cd610b --- /dev/null +++ b/src/add-ons/accelerants/3dfx/3dfx_cursor.cpp @@ -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 + + + +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; +} diff --git a/src/add-ons/accelerants/3dfx/3dfx_dpms.cpp b/src/add-ons/accelerants/3dfx/3dfx_dpms.cpp new file mode 100644 index 0000000000..30adbd4f36 --- /dev/null +++ b/src/add-ons/accelerants/3dfx/3dfx_dpms.cpp @@ -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; +} diff --git a/src/add-ons/accelerants/3dfx/3dfx_draw.cpp b/src/add-ons/accelerants/3dfx/3dfx_draw.cpp new file mode 100644 index 0000000000..106b62ed66 --- /dev/null +++ b/src/add-ons/accelerants/3dfx/3dfx_draw.cpp @@ -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++; + } +} diff --git a/src/add-ons/accelerants/3dfx/3dfx_edid.cpp b/src/add-ons/accelerants/3dfx/3dfx_edid.cpp new file mode 100644 index 0000000000..b89e2a7f13 --- /dev/null +++ b/src/add-ons/accelerants/3dfx/3dfx_edid.cpp @@ -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 +#include +#include + + + +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; +} diff --git a/src/add-ons/accelerants/3dfx/3dfx_init.cpp b/src/add-ons/accelerants/3dfx/3dfx_init.cpp new file mode 100644 index 0000000000..3f298f600a --- /dev/null +++ b/src/add-ons/accelerants/3dfx/3dfx_init.cpp @@ -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); +} diff --git a/src/add-ons/accelerants/3dfx/3dfx_mode.cpp b/src/add-ons/accelerants/3dfx/3dfx_mode.cpp new file mode 100644 index 0000000000..5b16c0b68e --- /dev/null +++ b/src/add-ons/accelerants/3dfx/3dfx_mode.cpp @@ -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 +#include + + +// 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; + } +} diff --git a/src/add-ons/accelerants/3dfx/3dfx_overlay.cpp b/src/add-ons/accelerants/3dfx/3dfx_overlay.cpp new file mode 100644 index 0000000000..1b07ff2795 --- /dev/null +++ b/src/add-ons/accelerants/3dfx/3dfx_overlay.cpp @@ -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); +} diff --git a/src/add-ons/accelerants/3dfx/Jamfile b/src/add-ons/accelerants/3dfx/Jamfile new file mode 100644 index 0000000000..7315962452 --- /dev/null +++ b/src/add-ons/accelerants/3dfx/Jamfile @@ -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 +; diff --git a/src/add-ons/accelerants/3dfx/accelerant.cpp b/src/add-ons/accelerants/3dfx/accelerant.cpp new file mode 100644 index 0000000000..6bddfd53d0 --- /dev/null +++ b/src/add-ons/accelerants/3dfx/accelerant.cpp @@ -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 +#include +#include + + + +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; +} diff --git a/src/add-ons/accelerants/3dfx/accelerant.h b/src/add-ons/accelerants/3dfx/accelerant.h new file mode 100644 index 0000000000..8742724146 --- /dev/null +++ b/src/add-ons/accelerants/3dfx/accelerant.h @@ -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 diff --git a/src/add-ons/accelerants/3dfx/cursor.cpp b/src/add-ons/accelerants/3dfx/cursor.cpp new file mode 100644 index 0000000000..e4860fba54 --- /dev/null +++ b/src/add-ons/accelerants/3dfx/cursor.cpp @@ -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); +} + diff --git a/src/add-ons/accelerants/3dfx/engine.cpp b/src/add-ons/accelerants/3dfx/engine.cpp new file mode 100644 index 0000000000..079190578a --- /dev/null +++ b/src/add-ons/accelerants/3dfx/engine.cpp @@ -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; +} + diff --git a/src/add-ons/accelerants/3dfx/hooks.cpp b/src/add-ons/accelerants/3dfx/hooks.cpp new file mode 100644 index 0000000000..9830fafccf --- /dev/null +++ b/src/add-ons/accelerants/3dfx/hooks.cpp @@ -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 +} diff --git a/src/add-ons/accelerants/3dfx/mode.cpp b/src/add-ons/accelerants/3dfx/mode.cpp new file mode 100644 index 0000000000..bae14c185a --- /dev/null +++ b/src/add-ons/accelerants/3dfx/mode.cpp @@ -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 // common accelerant header file + +#include +#include + + +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__ diff --git a/src/add-ons/accelerants/3dfx/overlay.cpp b/src/add-ons/accelerants/3dfx/overlay.cpp new file mode 100644 index 0000000000..a06e6c8454 --- /dev/null +++ b/src/add-ons/accelerants/3dfx/overlay.cpp @@ -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 + + + +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; +} diff --git a/src/add-ons/accelerants/Jamfile b/src/add-ons/accelerants/Jamfile index 09b18cbff2..a7743d5fc6 100644 --- a/src/add-ons/accelerants/Jamfile +++ b/src/add-ons/accelerants/Jamfile @@ -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 ; diff --git a/src/add-ons/kernel/drivers/graphics/3dfx/Jamfile b/src/add-ons/kernel/drivers/graphics/3dfx/Jamfile new file mode 100644 index 0000000000..24b65e43f4 --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/3dfx/Jamfile @@ -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 +; diff --git a/src/add-ons/kernel/drivers/graphics/3dfx/driver.cpp b/src/add-ons/kernel/drivers/graphics/3dfx/driver.cpp new file mode 100644 index 0000000000..b8cbfc8598 --- /dev/null +++ b/src/add-ons/kernel/drivers/graphics/3dfx/driver.cpp @@ -0,0 +1,563 @@ +/* + * Copyright 2007-2010 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT license. + * + * Authors: + * Gerald Zajac + */ + +#include +#include +#include +#include +#include +#include +#ifdef __HAIKU__ +#include +#include +#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; +} diff --git a/src/add-ons/kernel/drivers/graphics/Jamfile b/src/add-ons/kernel/drivers/graphics/Jamfile index 340fa13d75..0d6085f7f0 100644 --- a/src/add-ons/kernel/drivers/graphics/Jamfile +++ b/src/add-ons/kernel/drivers/graphics/Jamfile @@ -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 ;