driver: New intel 810 video driver

* Introduced by Gerald Zajac in #8615
* Will need reviewed, tested, and some style cleanup
* Not in images until steps above complete
This commit is contained in:
Gerald Zajac 2012-05-30 15:21:18 -05:00 committed by Alexander von Gluck IV
parent 9c02217342
commit e0ee3b7971
16 changed files with 2436 additions and 0 deletions

View File

@ -0,0 +1,110 @@
/*
* Copyright 2007-2012 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>
// 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); }
};
enum {
INTEL_GET_SHARED_DATA = B_DEVICE_OP_CODES_END + 234,
INTEL_DEVICE_NAME,
INTEL_GET_EDID,
};
struct DisplayModeEx : display_mode {
uint8 bitsPerPixel;
uint8 bytesPerPixel;
uint16 bytesPerRow; // number of bytes in one line/row
};
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
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. Addr's shared with all teams.
addr_t videoMemAddr; // virtual video memory addr
phys_addr_t videoMemPCI; // physical video memory addr
uint32 videoMemSize; // video memory size in bytes (for frame buffer).
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
// 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
edid1_info edidInfo;
bool bHaveEDID; // true = EDID info from device is in edidInfo
Benaphore engineLock; // for serializing access to the acceleration engine
};
#endif // DRIVERINTERFACE_H

View File

@ -4,6 +4,7 @@ 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 ;
SubInclude HAIKU_TOP src add-ons accelerants intel_810 ;
SubInclude HAIKU_TOP src add-ons accelerants intel_extreme ;
SubInclude HAIKU_TOP src add-ons accelerants matrox ;
SubInclude HAIKU_TOP src add-ons accelerants neomagic ;

View File

@ -0,0 +1,19 @@
SubDir HAIKU_TOP src add-ons accelerants intel_810 ;
UsePrivateHeaders graphics ;
UsePrivateHeaders [ FDirName graphics intel_810 ] ;
UsePrivateHeaders [ FDirName graphics common ] ;
Addon intel_810.accelerant :
accelerant.cpp
engine.cpp
hooks.cpp
mode.cpp
i810_dpms.cpp
i810_init.cpp
i810_mode.cpp
i810_watermark.cpp
: be libaccelerantscommon.a
;

View File

@ -0,0 +1,236 @@
/*
* Copyright 2007-2012 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 source files of accelerant
static uint32 videoValue;
static int32
SuppressArtifacts(void* dataPtr)
{
// The Intel 810 & 815 video chips create annoying artifacts which are
// most noticeable when the cursor is moved by itself or the user goes up
// and down through a menu. However, if a large number of video memory
// locations are accessed frequently like when the GLTeapot demo is
// running, the artifacts are greatly suppressed. Thus, that is the reason
// why this function accesses a large number of video memory locations
// frequently. Note that the accessed memory locations are at the end of
// the video memory. This is because some artifacts still occur at the
// top of the screen if the accessed memory is at the beginning of the
// video memory.
// Note that this function will reduce the general performance of a
// computer somewhat, but it is much less of a hit than if double
// buffering was used for the video. Base on the frame rate of the
// the GLTeapot demo, it is less than a 10% reduction.
SharedInfo& si = *((SharedInfo*)dataPtr);
while (true)
{
uint32* src = ((uint32*)(si.videoMemAddr)) + si.videoMemSize / 4 - 1;
uint32 count = 65000;
while (count-- > 0)
videoValue = *src--;
snooze(30000); // sleep for 30 msec
}
return 0;
}
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, INTEL_GET_SHARED_DATA,
&sharedArea, sizeof(sharedArea));
if (result != B_OK)
return result;
gInfo.sharedInfoArea = clone_area("i810 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("i810 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 = I810_Init(); // perform init related to current chip
if (result == B_OK) {
result = si.engineLock.Init("i810 engine lock");
if (result == B_OK) {
// Ensure that this function won't be executed again
// (copies should be clones)
si.bAccelerantInUse = true;
thread_id threadID = spawn_thread(SuppressArtifacts,
"SuppressArtifacts_Thread", B_DISPLAY_PRIORITY,
gInfo.sharedInfo);
result = resume_thread(threadID);
}
}
}
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, INTEL_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("i810 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, "Intel 810/815 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,117 @@
/*
* Copyright 2007-2012 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("i810: " x)
#else
# define TRACE(x...) ;
#endif
// 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 I810_SetIndexedColors(uint count, uint8 first, uint8* color_data,
uint32 flags);
status_t GetEdidInfo(void* info, size_t size, uint32* _version);
// DPMS
uint32 I810_DPMSCapabilities(void);
uint32 I810_GetDPMSMode(void);
status_t I810_SetDPMSMode(uint32 dpms_flags);
// 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);
#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));
bool IsModeUsable(const display_mode* mode);
// Intel 810 functions.
status_t I810_Init(void);
bool I810_GetColorSpaceParams(int colorSpace, uint8& bpp,
uint32& maxPixelClk);
uint32 I810_GetWatermark(const DisplayModeEx& mode);
void I810_AdjustFrame(const DisplayModeEx& mode);
status_t I810_SetDisplayMode(const DisplayModeEx& mode);
#endif // _ACCELERANT_H

View File

@ -0,0 +1,81 @@
/*
* Copyright 2007-2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
#include "accelerant.h"
#include "i810_regs.h"
static engine_token sEngineToken = { 1, B_2D_ACCELERATION, NULL };
uint32
AccelerantEngineCount(void)
{
return 1;
}
status_t
AcquireEngine(uint32 capabilities, uint32 maxWait,
sync_token* syncToken, engine_token** engineToken)
{
(void)capabilities; // avoid compiler warning for unused arg
(void)maxWait; // avoid compiler warning for unused arg
if (gInfo.sharedInfo->engineLock.Acquire() != B_OK)
return B_ERROR;
if (syncToken)
SyncToToken(syncToken);
*engineToken = &sEngineToken;
return B_OK;
}
status_t
ReleaseEngine(engine_token* engineToken, sync_token* syncToken)
{
if (syncToken)
GetSyncToken(engineToken, syncToken);
gInfo.sharedInfo->engineLock.Release();
return B_OK;
}
void
WaitEngineIdle(void)
{
// Wait until engine is idle.
int k = 10000000;
while ((INREG16(INST_DONE) & 0x7B) != 0x7B && k > 0)
k--;
}
status_t
GetSyncToken(engine_token* engineToken, sync_token* syncToken)
{
syncToken->engine_id = engineToken->engine_id;
syncToken->counter = 0;
return B_OK;
}
status_t
SyncToToken(sync_token* syncToken)
{
(void)syncToken; // avoid compiler warning for unused arg
WaitEngineIdle();
return B_OK;
}

View File

@ -0,0 +1,84 @@
/*
* Copyright 2008-2012 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_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*)(I810_SetIndexedColors);
case B_GET_TIMING_CONSTRAINTS:
return NULL;
// DPMS
case B_DPMS_CAPABILITIES:
return (void*)(I810_DPMSCapabilities);
case B_DPMS_MODE:
return (void*)(I810_GetDPMSMode);
case B_SET_DPMS_MODE:
return (void*)(I810_SetDPMSMode);
// 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;
}
return NULL; // Return null pointer for any feature not handled above
}

View File

@ -0,0 +1,101 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
/*!
Haiku Intel-810 video driver was adapted from the X.org intel driver which
has the following copyright.
Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
All Rights Reserved.
*/
#include "accelerant.h"
#include "i810_regs.h"
#define DPMS_SYNC_SELECT 0x5002
#define H_SYNC_OFF 0x02
#define V_SYNC_OFF 0x08
uint32
I810_DPMSCapabilities(void)
{
// Return DPMS modes supported by this device.
return B_DPMS_ON | B_DPMS_STAND_BY | B_DPMS_SUSPEND | B_DPMS_OFF;
}
uint32
I810_GetDPMSMode(void)
{
// Return the current DPMS mode.
uint32 tmp = INREG8(DPMS_SYNC_SELECT) & (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("I810_DPMSMode() mode: %d\n", mode);
return mode;
}
status_t
I810_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("I810_SetDPMSMode() mode: %d\n", dpmsMode);
uint8 seq01 = ReadSeqReg(1) & ~0x20;
uint8 dpmsSyncSelect = 0;
switch (dpmsMode) {
case B_DPMS_ON:
// Screen: On; HSync: On, VSync: On.
break;
case B_DPMS_STAND_BY:
// Screen: Off; HSync: Off, VSync: On.
seq01 |= 0x20;
dpmsSyncSelect = H_SYNC_OFF;
break;
case B_DPMS_SUSPEND:
// Screen: Off; HSync: On, VSync: Off.
seq01 |= 0x20;
dpmsSyncSelect = V_SYNC_OFF;
break;
case B_DPMS_OFF:
// Screen: Off; HSync: Off, VSync: Off.
seq01 |= 0x20;
dpmsSyncSelect = H_SYNC_OFF | V_SYNC_OFF;
break;
default:
TRACE("Invalid DPMS mode %d\n", dpmsMode);
return B_ERROR;
}
WriteSeqReg(1, seq01); // turn the screen on/off
OUTREG8(DPMS_SYNC_SELECT, dpmsSyncSelect); // set DPMS mode
return B_OK;
}

View File

@ -0,0 +1,70 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
/*!
Haiku Intel-810 video driver was adapted from the X.org intel driver which
has the following copyright.
Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
All Rights Reserved.
*/
#include "accelerant.h"
#include "i810_regs.h"
bool
I810_GetColorSpaceParams(int colorSpace, uint8& bitsPerPixel,
uint32& maxPixelClock)
{
// Get parameters for a color space which is supported by the i810 chips.
// Argument maxPixelClock is in KHz.
// Return true if the color space is supported; else return false.
switch (colorSpace) {
case B_RGB16:
bitsPerPixel = 16;
maxPixelClock = 163000;
break;
break;
case B_CMAP8:
bitsPerPixel = 8;
maxPixelClock = 203000;
break;
default:
TRACE("Unsupported color space: 0x%X\n", colorSpace);
return false;
}
return true;
}
status_t
I810_Init(void)
{
TRACE("I810_Init()\n");
SharedInfo& si = *gInfo.sharedInfo;
// Use all of video memory for the frame buffer.
si.maxFrameBufferSize = si.videoMemSize;
// Set up the array of the supported color spaces.
si.colorSpaces[0] = B_CMAP8;
si.colorSpaces[1] = B_RGB16;
si.colorSpaceCount = 2;
// Setup the mode list.
return CreateModeList(IsModeUsable);
}

View File

@ -0,0 +1,288 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
/*!
Haiku Intel-810 video driver was adapted from the X.org intel driver which
has the following copyright.
Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
All Rights Reserved.
*/
#include "accelerant.h"
#include "i810_regs.h"
#include <create_display_modes.h> // common accelerant header file
#include <math.h>
#include <unistd.h>
// I810_CalcVCLK -- Determine closest clock frequency to the one requested.
#define MAX_VCO_FREQ 600.0
#define TARGET_MAX_N 30
#define REF_FREQ 24.0
#define CALC_VCLK(m,n,p) (double)m / ((double)n * (1 << p)) * 4 * REF_FREQ
static void
CalcVCLK(double freq, uint16& clkM, uint16& clkN, uint16& clkP) {
int m, n, p;
double f_out, f_best;
double f_err;
double f_vco;
int m_best = 0, n_best = 0, p_best = 0;
double f_target = freq;
double errMax = 0.005;
double errTarget = 0.001;
double errBest = 999999.0;
p_best = p = int(log(MAX_VCO_FREQ / f_target) / log((double)2));
// Make sure p is within range.
if (p_best > 5) {
p_best = p = 5;
}
f_vco = f_target * (1 << p);
n = 2;
do {
n++;
m = int(f_vco / (REF_FREQ / (double)n) / (double)4.0 + 0.5);
if (m < 3)
m = 3;
f_out = CALC_VCLK(m, n, p);
f_err = 1.0 - (f_target / f_out);
if (fabs(f_err) < errMax) {
m_best = m;
n_best = n;
f_best = f_out;
errBest = f_err;
}
} while ((fabs(f_err) >= errTarget) &&
((n <= TARGET_MAX_N) || (fabs(errBest) > errMax)));
if (fabs(f_err) < errTarget) {
m_best = m;
n_best = n;
}
clkM = (m_best - 2) & 0x3FF;
clkN = (n_best - 2) & 0x3FF;
clkP = (p_best << 4);
TRACE("Setting dot clock to %.1f MHz [ 0x%x 0x%x 0x%x ] [ %d %d %d ]\n",
CALC_VCLK(m_best, n_best, p_best),
clkM, clkN, clkP, m_best, n_best, p_best);
}
static void
SetCrtcTimingValues(const DisplayModeEx& mode)
{
// Set the timing values for CRTC registers cr00 to cr18, and some extended
// CRTC registers.
int hTotal = mode.timing.h_total / 8 - 5;
int hDisp_e = mode.timing.h_display / 8 - 1;
int hSync_s = mode.timing.h_sync_start / 8;
int hSync_e = mode.timing.h_sync_end / 8;
int hBlank_s = hDisp_e + 1; // start of horizontal blanking
int hBlank_e = hTotal; // 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; // start of vertical blanking
int vBlank_e = vTotal; // end of vertical blanking
uint16 offset = mode.bytesPerRow / 8;
// 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] = offset;
crtc[0x14] = 0x00;
crtc[0x15] = vBlank_s;
crtc[0x16] = vBlank_e;
crtc[0x17] = 0xc3;
crtc[0x18] = 0xff;
// Set the standard CRTC vga regs; however, before setting them, unlock
// CRTC reg's 0-7 by clearing bit 7 of cr11
WriteCrtcReg(0x11, crtc[0x11] & ~0x80);
for (uint8 j = 0; j <= 0x18; j++)
WriteCrtcReg(j, crtc[j]);
// Set the extended CRTC reg's.
WriteCrtcReg(EXT_VERT_TOTAL, vTotal >> 8);
WriteCrtcReg(EXT_VERT_DISPLAY, vDisp_e >> 8);
WriteCrtcReg(EXT_VERT_SYNC_START, vSync_s >> 8);
WriteCrtcReg(EXT_VERT_BLANK_START, vBlank_s >> 8);
WriteCrtcReg(EXT_HORIZ_TOTAL, hTotal >> 8);
WriteCrtcReg(EXT_HORIZ_BLANK, (hBlank_e & 0x40) >> 6);
WriteCrtcReg(EXT_OFFSET, offset >> 8);
WriteCrtcReg(INTERLACE_CNTL, INTERLACE_DISABLE); // turn off interlace
// Enable high resolution mode.
WriteCrtcReg(IO_CTNL, ReadCrtcReg(IO_CTNL) | EXTENDED_CRTC_CNTL);
}
status_t
I810_SetDisplayMode(const DisplayModeEx& mode)
{
if (mode.bitsPerPixel != 8 && mode.bitsPerPixel != 16) {
// Only 8 & 16 bits/pixel are suppoted.
TRACE("Unsupported color depth: %d bpp\n", mode.bitsPerPixel);
return B_ERROR;
}
snooze(50000);
// Turn off DRAM refresh.
uint8 temp = INREG8(DRAM_ROW_CNTL_HI) & ~DRAM_REFRESH_RATE;
OUTREG8(DRAM_ROW_CNTL_HI, temp | DRAM_REFRESH_DISABLE);
snooze(1000); // wait 1 ms
// Calculate the VCLK that most closely matches the requested pixel clock,
// and then set the M, N, and P values.
uint16 m, n, p;
CalcVCLK(mode.timing.pixel_clock / 1000.0, m, n, p);
OUTREG16(VCLK2_VCO_M, m);
OUTREG16(VCLK2_VCO_N, n);
OUTREG8(VCLK2_VCO_DIV_SEL, p);
// Setup HSYNC & VSYNC polarity and select clock source 2 (0x08) for
// programmable PLL.
uint8 miscOutReg = 0x08 | 0x01;
if (!(mode.timing.flags & B_POSITIVE_HSYNC))
miscOutReg |= 0x40;
if (!(mode.timing.flags & B_POSITIVE_VSYNC))
miscOutReg |= 0x80;
OUTREG8(MISC_OUT_W, miscOutReg);
SetCrtcTimingValues(mode);
OUTREG32(MEM_MODE, INREG32(MEM_MODE) | 4);
// Set the address mapping to use the frame buffer memory mapped via the
// GTT table instead of the VGA buffer.
uint8 addrMapping = ReadGraphReg(ADDRESS_MAPPING);
addrMapping &= 0xE0; // preserve reserved bits 7:5
addrMapping |= (GTT_MEM_MAP_ENABLE | LINEAR_MODE_ENABLE);
WriteGraphReg(ADDRESS_MAPPING, addrMapping);
// Turn on DRAM refresh.
temp = INREG8(DRAM_ROW_CNTL_HI) & ~DRAM_REFRESH_RATE;
OUTREG8(DRAM_ROW_CNTL_HI, temp | DRAM_REFRESH_60HZ);
temp = INREG8(BITBLT_CNTL) & ~COLEXP_MODE;
temp |= (mode.bitsPerPixel == 8 ? COLEXP_8BPP : COLEXP_16BPP);
OUTREG8(BITBLT_CNTL, temp);
// Turn on 8 bit dac mode so that the indexed colors are displayed properly,
// and put display in high resolution mode.
uint32 temp32 = INREG32(PIXPIPE_CONFIG) & 0xF3E062FC;
temp32 |= (DAC_8_BIT | HIRES_MODE | NO_BLANK_DELAY |
(mode.bitsPerPixel == 8 ? DISPLAY_8BPP_MODE : DISPLAY_16BPP_MODE));
OUTREG32(PIXPIPE_CONFIG, temp32);
OUTREG16(EIR, 0);
temp32 = INREG32(FWATER_BLC);
temp32 &= ~(LM_BURST_LENGTH | LM_FIFO_WATERMARK |
MM_BURST_LENGTH | MM_FIFO_WATERMARK);
temp32 |= I810_GetWatermark(mode);
OUTREG32(FWATER_BLC, temp32);
// Enable high resolution mode.
WriteCrtcReg(IO_CTNL, ReadCrtcReg(IO_CTNL) | EXTENDED_CRTC_CNTL);
I810_AdjustFrame(mode);
return B_OK;
}
void
I810_AdjustFrame(const DisplayModeEx& mode)
{
// Adjust start address in frame buffer.
uint32 address = ((mode.v_display_start * mode.virtual_width
+ mode.h_display_start) * mode.bytesPerPixel) >> 2;
WriteCrtcReg(START_ADDR_LO, address & 0xff);
WriteCrtcReg(START_ADDR_HI, (address >> 8) & 0xff);
WriteCrtcReg(EXT_START_ADDR_HI, (address >> 22) & 0xff);
WriteCrtcReg(EXT_START_ADDR,
((address >> 16) & 0x3f) | EXT_START_ADDR_ENABLE);
}
void
I810_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 ;
OUTREG8(DAC_MASK, 0xff);
OUTREG8(DAC_W_INDEX, first); // initial color index
while (count--) {
OUTREG8(DAC_DATA, colorData[0]); // red
OUTREG8(DAC_DATA, colorData[1]); // green
OUTREG8(DAC_DATA, colorData[2]); // blue
colorData += 3;
}
}

View File

@ -0,0 +1,163 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
/*!
Haiku Intel-810 video driver was adapted from the X.org intel driver which
has the following copyright.
Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
All Rights Reserved.
*/
#ifndef __I810_REGS_H__
#define __I810_REGS_H__
// CRT Controller Registers.
#define START_ADDR_HI 0x0C
#define START_ADDR_LO 0x0D
#define VERT_SYNC_END 0x11
#define EXT_VERT_TOTAL 0x30
#define EXT_VERT_DISPLAY 0x31
#define EXT_VERT_SYNC_START 0x32
#define EXT_VERT_BLANK_START 0x33
#define EXT_HORIZ_TOTAL 0x35
#define EXT_HORIZ_BLANK 0x39
#define EXT_START_ADDR 0x40
#define EXT_START_ADDR_ENABLE 0x80
#define EXT_OFFSET 0x41
#define EXT_START_ADDR_HI 0x42
#define INTERLACE_CNTL 0x70
#define INTERLACE_ENABLE 0x80
#define INTERLACE_DISABLE 0x00
// CR80 - IO Control
#define IO_CTNL 0x80
#define EXTENDED_ATTR_CNTL 0x02
#define EXTENDED_CRTC_CNTL 0x01
// GR10 - Address mapping
#define ADDRESS_MAPPING 0x10
#define PAGE_TO_LOCAL_MEM_ENABLE 0x10
#define GTT_MEM_MAP_ENABLE 0x08
#define PACKED_MODE_ENABLE 0x04
#define LINEAR_MODE_ENABLE 0x02
#define PAGE_MAPPING_ENABLE 0x01
#define FENCE 0x2000
#define INST_DONE 0x2090
// General error reporting regs.
#define EIR 0x20B0
#define EMR 0x20B4
#define ESR 0x20B8
// FIFO Watermark and Burst Length Control Register.
#define FWATER_BLC 0x20d8
#define MM_BURST_LENGTH 0x00700000
#define MM_FIFO_WATERMARK 0x0001F000
#define LM_BURST_LENGTH 0x00000700
#define LM_FIFO_WATERMARK 0x0000001F
#define MEM_MODE 0x020DC
#define DRAM_ROW_CNTL_HI 0x3002
#define DRAM_REFRESH_RATE 0x18
#define DRAM_REFRESH_DISABLE 0x00
#define DRAM_REFRESH_60HZ 0x08
#define VCLK2_VCO_M 0x6008
#define VCLK2_VCO_N 0x600a
#define VCLK2_VCO_DIV_SEL 0x6012
#define PIXPIPE_CONFIG 0x70008
#define NO_BLANK_DELAY 0x100000
#define DISPLAY_8BPP_MODE 0x020000
#define DISPLAY_15BPP_MODE 0x040000
#define DISPLAY_16BPP_MODE 0x050000
#define DAC_8_BIT 0x008000
#define HIRES_MODE 0x000001
// Blitter control.
#define BITBLT_CNTL 0x7000c
#define COLEXP_MODE 0x30
#define COLEXP_8BPP 0x00
#define COLEXP_16BPP 0x10
// Color Palette Registers.
#define DAC_MASK 0x3C6
#define DAC_W_INDEX 0x3C8
#define DAC_DATA 0x3C9
#define MISC_OUT_R 0x3CC // read
#define MISC_OUT_W 0x3C2 // write
#define SEQ_INDEX 0x3C4
#define SEQ_DATA 0x3C5
#define GRAPH_INDEX 0x3CE
#define GRAPH_DATA 0x3CF
#define CRTC_INDEX 0x3D4
#define CRTC_DATA 0x3D5
// 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)))
static inline uint8 ReadCrtcReg(uint8 index)
{
OUTREG8(CRTC_INDEX, index);
return INREG8(CRTC_DATA);
}
static inline void WriteCrtcReg(uint8 index, uint8 value)
{
OUTREG8(CRTC_INDEX, index);
OUTREG8(CRTC_DATA, value);
}
static inline uint8 ReadGraphReg(uint8 index)
{
OUTREG8(GRAPH_INDEX, index);
return INREG8(GRAPH_DATA);
}
static inline void WriteGraphReg(uint8 index, uint8 value)
{
OUTREG8(GRAPH_INDEX, index);
OUTREG8(GRAPH_DATA, value);
}
static inline uint8 ReadSeqReg(uint8 index)
{
OUTREG8(SEQ_INDEX, index);
return INREG8(SEQ_DATA);
}
static inline void WriteSeqReg(uint8 index, uint8 value)
{
OUTREG8(SEQ_INDEX, index);
OUTREG8(SEQ_DATA, value);
}
#endif // __I810_REGS_H__

View File

@ -0,0 +1,143 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
// The code in this file was adapted from the X.org intel driver which had
// 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"
struct WatermarkInfo {
double freq;
uint32 watermark;
};
static WatermarkInfo watermarks_8[] = {
{ 0, 0x22003000},
{25.2, 0x22003000},
{28.0, 0x22003000},
{31.5, 0x22003000},
{36.0, 0x22007000},
{40.0, 0x22007000},
{45.0, 0x22007000},
{49.5, 0x22008000},
{50.0, 0x22008000},
{56.3, 0x22008000},
{65.0, 0x22008000},
{75.0, 0x22008000},
{78.8, 0x22008000},
{80.0, 0x22008000},
{94.0, 0x22008000},
{96.0, 0x22107000},
{99.0, 0x22107000},
{108.0, 0x22107000},
{121.0, 0x22107000},
{128.9, 0x22107000},
{132.0, 0x22109000},
{135.0, 0x22109000},
{157.5, 0x2210b000},
{162.0, 0x2210b000},
{175.5, 0x2210b000},
{189.0, 0x2220e000},
{202.5, 0x2220e000}
};
static WatermarkInfo watermarks_16[] = {
{ 0, 0x22004000},
{25.2, 0x22006000},
{28.0, 0x22006000},
{31.5, 0x22007000},
{36.0, 0x22007000},
{40.0, 0x22007000},
{45.0, 0x22007000},
{49.5, 0x22009000},
{50.0, 0x22009000},
{56.3, 0x22108000},
{65.0, 0x2210e000},
{75.0, 0x2210e000},
{78.8, 0x2210e000},
{80.0, 0x22210000},
{94.5, 0x22210000},
{96.0, 0x22210000},
{99.0, 0x22210000},
{108.0, 0x22210000},
{121.0, 0x22210000},
{128.9, 0x22210000},
{132.0, 0x22314000},
{135.0, 0x22314000},
{157.5, 0x22415000},
{162.0, 0x22416000},
{175.5, 0x22416000},
{189.0, 0x22416000},
{195.0, 0x22416000},
{202.5, 0x22416000}
};
uint32
I810_GetWatermark(const DisplayModeEx& mode)
{
WatermarkInfo *table;
uint32 tableLen;
// Get burst length and FIFO watermark based upon the bus frequency and
// pixel clock.
switch (mode.bitsPerPixel) {
case 8:
table = watermarks_8;
tableLen = ARRAY_SIZE(watermarks_8);
break;
case 16:
table = watermarks_16;
tableLen = ARRAY_SIZE(watermarks_16);
break;
default:
return 0;
}
uint32 i;
double clockFreq = mode.timing.pixel_clock / 1000.0;
for (i = 0; i < tableLen && table[i].freq < clockFreq; i++)
;
if (i == tableLen)
i--;
TRACE("chosen watermark 0x%lx (freq %f)\n", table[i].watermark,
table[i].freq);
return table[i].watermark;
}

View File

@ -0,0 +1,339 @@
/*
* Copyright 2007-2012 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;
uint32 maxPixelClock;
if (!I810_GetColorSpaceParams(mode->space, bitsPerPixel, maxPixelClock))
return false;
// Is there enough frame buffer memory to handle the mode?
if (!IsThereEnoughFBMemory(mode, bitsPerPixel))
return false;
if (mode->timing.pixel_clock > 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;
// Reject modes with a width of 640 and a height < 480 since they do not
// work properly with the i810 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.
si.bHaveEDID = false;
if (!si.bHaveEDID) {
edid1_raw rawEdid; // raw EDID info to obtain
if (ioctl(gInfo.deviceFileDesc, INTEL_GET_EDID, &rawEdid,
sizeof(rawEdid)) == B_OK) {
if (rawEdid.version.version != 1 || rawEdid.version.revision > 4) {
TRACE("CreateModeList(); EDID version %d.%d out of range\n",
rawEdid.version.version, rawEdid.version.revision);
} else {
edid_decode(&si.edidInfo, &rawEdid); // decode & save EDID info
si.bHaveEDID = true;
}
}
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("i810 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
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);
// Search the mode list for the specified mode.
uint32 modeCount = gInfo.sharedInfo->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;
uint32 maxPixelClock;
if (!I810_GetColorSpaceParams(mode.space, mode.bitsPerPixel, maxPixelClock))
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 = I810_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;
I810_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));
pFBC->frame_buffer_dma = (void*)((addr_t)(si.videoMemPCI));
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.
uint8 bitsPerPixel;
uint32 maxPixelClock;
if (!I810_GetColorSpaceParams(mode->space, bitsPerPixel, maxPixelClock))
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 > maxPixelClock)
return B_ERROR;
*low = lowClock;
}
if (high != NULL)
*high = 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

@ -4,6 +4,7 @@ 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 ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics intel_810 ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics intel_extreme ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics matrox ;
SubInclude HAIKU_TOP src add-ons kernel drivers graphics neomagic ;

View File

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

View File

@ -0,0 +1,674 @@
/*
* Copyright 2007-2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT license.
*
* Authors:
* Gerald Zajac
*/
#include <AGP.h>
#include <KernelExport.h>
#include <PCI.h>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <graphic_driver.h>
#include <boot_item.h>
#include <arch/x86/vm86.h>
#include "DriverInterface.h"
#undef TRACE
#ifdef ENABLE_DEBUG_TRACE
# define TRACE(x...) dprintf("i810: " x)
#else
# define TRACE(x...) ;
#endif
#define ACCELERANT_NAME "intel_810.accelerant"
#define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1))
#define MAX_DEVICES 4
#define DEVICE_FORMAT "%04X_%04X_%02X%02X%02X"
#define VENDOR_ID 0x8086 // Intel vendor ID
struct ChipInfo {
uint16 chipID; // PCI device id of the chip
const char* chipName; // user recognizable name (must be < 32 chars)
};
// This table maps a PCI device ID to a chip type identifier and the chip name.
static const ChipInfo chipTable[] = {
{ 0x7121, "i810" },
{ 0x7123, "i810-dc100" },
{ 0x7125, "i810e" },
{ 0x1132, "i815" },
{ 0, NULL }
};
struct DeviceInfo {
uint32 openCount; // count of how many times device has been opened
int32 flags;
area_id sharedArea; // area shared between driver and 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
area_id gttArea; // area used for GTT
addr_t gttAddr; // virtual address of GTT
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
};
// Video chip register definitions.
//=================================
#define INTERRUPT_ENABLED 0x020a0
#define INTERRUPT_MASK 0x020a8
// Graphics address translation table.
#define PAGE_TABLE_CONTROL 0x02020
#define PAGE_TABLE_ENABLED 0x01
#define PTE_BASE 0x10000
#define PTE_VALID 0x01
// Macros for memory mapped I/O.
//==============================
#define INREG16(addr) (*((vuint16*)(di.regs + (addr))))
#define INREG32(addr) (*((vuint32*)(di.regs + (addr))))
#define OUTREG16(addr, val) (*((vuint16*)(di.regs + (addr))) = (val))
#define OUTREG32(addr, val) (*((vuint32*)(di.regs + (addr))) = (val))
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
GetEdidFromBIOS(edid1_raw& edidRaw)
{
// Get the EDID info from the video BIOS, and return B_OK if successful.
#define ADDRESS_SEGMENT(address) ((addr_t)(address) >> 4)
#define ADDRESS_OFFSET(address) ((addr_t)(address) & 0xf)
vm86_state vmState;
status_t status = vm86_prepare(&vmState, 0x2000);
if (status != B_OK) {
TRACE("GetEdidFromBIOS(); vm86_prepare() failed, status: 0x%lx\n",
status);
return status;
}
vmState.regs.eax = 0x4f15;
vmState.regs.ebx = 0; // 0 = report DDC service
vmState.regs.ecx = 0;
vmState.regs.es = 0;
vmState.regs.edi = 0;
status = vm86_do_int(&vmState, 0x10);
if (status == B_OK) {
// AH contains the error code, and AL determines wether or not the
// function is supported.
if (vmState.regs.eax != 0x4f)
status = B_NOT_SUPPORTED;
// Test if DDC is supported by the monitor.
if ((vmState.regs.ebx & 3) == 0)
status = B_NOT_SUPPORTED;
}
if (status == B_OK) {
// According to the author of the vm86 functions, the address of any
// object to receive data must be >= 0x1000 and within the ram size
// specified in the second argument of the vm86_prepare() call above.
// Thus, the address of the struct to receive the EDID info is set to
// 0x1000.
edid1_raw* edid = (edid1_raw*)0x1000;
vmState.regs.eax = 0x4f15;
vmState.regs.ebx = 1; // 1 = read EDID
vmState.regs.ecx = 0;
vmState.regs.edx = 0;
vmState.regs.es = ADDRESS_SEGMENT(edid);
vmState.regs.edi = ADDRESS_OFFSET(edid);
status = vm86_do_int(&vmState, 0x10);
if (status == B_OK) {
if (vmState.regs.eax != 0x4f) {
status = B_NOT_SUPPORTED;
} else {
// Copy the EDID info to the caller's location, and compute the
// checksum of the EDID info while copying.
uint8 sum = 0;
uint8 allOr = 0;
uint8* dest = (uint8*)&edidRaw;
uint8* src = (uint8*)edid;
for (uint32 j = 0; j < sizeof(edidRaw); j++) {
sum += *src;
allOr |= *src;
*dest++ = *src++;
}
if (allOr == 0) {
TRACE("GetEdidFromBIOS(); EDID info contains only zeros\n");
status = B_ERROR;
} else if (sum != 0) {
TRACE("GetEdidFromBIOS(); Checksum error in EDID info\n");
status = B_ERROR;
}
}
}
}
vm86_cleanup(&vmState);
TRACE("GetEdidFromBIOS() status: 0x%lx\n", status);
return status;
}
static status_t
InitDevice(DeviceInfo& di)
{
// Perform initialization and mapping of the device, and return B_OK if
// sucessful; else, return error code.
TRACE("enter InitDevice()\n");
// Create the area for shared info with NO user-space read or write
// permissions, to prevent accidental damage.
size_t sharedSize = (sizeof(SharedInfo) + 7) & ~7;
di.sharedArea = create_area("i810 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);
si.regsArea = -1; // indicate area has not yet been created
si.videoMemArea = -1;
pci_info& pciInfo = di.pciInfo;
si.vendorID = pciInfo.vendor_id;
si.deviceID = pciInfo.device_id;
si.revision = pciInfo.revision;
strcpy(si.chipName, di.pChipInfo->chipName);
// 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 MMIO register area.
phys_addr_t regsBase = pciInfo.u.h0.base_registers[1];
uint32 regAreaSize = pciInfo.u.h0.base_register_sizes[1];
si.regsArea = map_physical_memory("i810 mmio registers",
regsBase,
regAreaSize,
B_ANY_KERNEL_ADDRESS,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
(void**)&di.regs);
if (si.regsArea < 0) {
TRACE("Unable to map MMIO, error: 0x%lx\n", si.regsArea);
return si.regsArea;
}
// Allocate memory for the GTT which must be 64K for the 810/815 chips.
uint32 gttSize = 64 * 1024;
di.gttArea = create_area("GTT memory", (void**) &(di.gttAddr),
B_ANY_KERNEL_ADDRESS, gttSize, B_FULL_LOCK | B_CONTIGUOUS,
B_READ_AREA | B_WRITE_AREA);
if (di.gttArea < B_OK) {
TRACE("Unable to create GTT, error: 0x%lx\n", di.gttArea);
return B_NO_MEMORY;
}
memset((void*)(di.gttAddr), 0, gttSize);
// Get the physical address of the GTT, and set GTT address in the chip.
physical_entry entry;
status_t status = get_memory_map((void *)(di.gttAddr),
B_PAGE_SIZE, &entry, 1);
if (status < B_OK) {
TRACE("Unable to get physical address of GTT, error: 0x%lx\n", status);
return status;
}
OUTREG32(PAGE_TABLE_CONTROL, entry.address | PAGE_TABLE_ENABLED);
INREG32(PAGE_TABLE_CONTROL);
// Allocate video memory to be used for the frame buffer.
si.videoMemSize = 4 * 1024 * 1024;
si.videoMemArea = create_area("video memory", (void**)&(si.videoMemAddr),
B_ANY_ADDRESS, si.videoMemSize, B_FULL_LOCK,
B_READ_AREA | B_WRITE_AREA);
if (si.videoMemArea < B_OK) {
TRACE("Unable to create video memory, error: 0x%lx\n", si.videoMemArea);
return B_NO_MEMORY;
}
// Get the physical address of each page of the video memory, and put
// the physical address of each page into the GTT table.
for (uint32 offset = 0; offset < si.videoMemSize; offset += B_PAGE_SIZE) {
status = get_memory_map((void *)(si.videoMemAddr + offset),
B_PAGE_SIZE, &entry, 1);
if (status < B_OK) {
TRACE("Unable to get physical address of video memory page, error:"
" 0x%lx offset: %ld\n", status, offset);
return status;
}
if (offset == 0)
si.videoMemPCI = entry.address;
OUTREG32(PTE_BASE + ((offset / B_PAGE_SIZE) * 4),
entry.address | PTE_VALID);
}
TRACE("InitDevice() exit OK\n");
return B_OK;
}
static void
DeleteAreas(DeviceInfo& di)
{
// Delete all areas that were created.
if (di.sharedArea >= 0 && di.sharedInfo != NULL) {
SharedInfo& si = *(di.sharedInfo);
if (si.regsArea >= 0)
delete_area(si.regsArea);
if (si.videoMemArea >= 0)
delete_area(si.videoMemArea);
}
if (di.gttArea >= 0)
delete_area(di.gttArea);
di.gttArea = -1;
di.gttAddr = (addr_t)NULL;
if (di.sharedArea >= 0)
delete_area(di.sharedArea);
di.sharedArea = -1;
di.sharedInfo = NULL;
}
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.
status_t status = get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI);
if (status != B_OK) {
TRACE("PCI module unavailable, error 0x%lx\n", status);
return status;
}
// 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.
status_t status = get_module(B_PCI_MODULE_NAME, (module_info**)&gPCI);
if (status != B_OK) {
TRACE("PCI module unavailable, error 0x%lx\n", status);
return status;
}
status = gLock.Init("i810 driver lock");
if (status < B_OK) {
put_module(B_AGP_GART_MODULE_NAME);
put_module(B_PCI_MODULE_NAME);
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.gttArea = -1; // indicate GTT area not yet created
di.gttAddr = (addr_t)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_AGP_GART_MODULE_NAME);
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);
if (status < B_OK)
DeleteAreas(di); // error occurred; delete any areas created
}
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)
DeleteAreas(di);
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);
TRACE("device_ioctl(); ioctl: %lu, buffer: 0x%08lx, bufLen: %lu\n", msg,
(uint32)buffer, bufferLength);
switch (msg) {
case B_GET_ACCELERANT_SIGNATURE:
strcpy((char*)buffer, ACCELERANT_NAME);
TRACE("Intel 810 accelerant: %s\n", ACCELERANT_NAME);
return B_OK;
case INTEL_DEVICE_NAME:
strncpy((char*)buffer, di.name, B_OS_NAME_LENGTH);
((char*)buffer)[B_OS_NAME_LENGTH -1] = '\0';
return B_OK;
case INTEL_GET_SHARED_DATA:
if (bufferLength != sizeof(area_id))
return B_BAD_DATA;
*((area_id*)buffer) = di.sharedArea;
return B_OK;
case INTEL_GET_EDID:
{
if (bufferLength != sizeof(edid1_raw))
return B_BAD_DATA;
edid1_raw rawEdid;
status_t status = GetEdidFromBIOS(rawEdid);
if (status == B_OK)
user_memcpy((edid1_raw*)buffer, &rawEdid, sizeof(rawEdid));
return status;
}
}
return B_DEV_INVALID_IOCTL;
}