home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
NeXTSTEP 3.2 (Developer)
/
NS_dev_3.2.iso
/
NextDeveloper
/
Examples
/
DriverKit
/
ATI
/
ATI_reloc.tproj
/
ATI.m
< prev
next >
Wrap
Text File
|
1993-08-30
|
20KB
|
705 lines
/* Copyright (c) 1992, 1993 NeXT Computer, Inc. All rights reserved.
*
* ATI.m - ATI display driver.
*
* HISTORY
* 07 Oct 92 Joe Pasqua
* Created.
* 01 June 93 Mike Paquette
* Rewrite: Convert from Corsair device driver to general MACH32 chipset
* driver. Add support for multiple DACs and CRT setups.
* Add 8 bit monochrome support. Add support for non-Cosair
* implementations.
* 7 July 1993 Derek B Clegg
* Cleaned up for external release.
*/
#import <driverkit/generalFuncs.h>
#import <driverkit/i386/ioPorts.h>
#import <driverkit/i386/directDevice.h>
#import <driverkit/i386/IOEISADeviceDescription.h>
#import "ATI.h"
#define ONE_MEG (1024 * 1024)
@implementation ATI
static void
UnlockShadowSet(int set)
{
outb(SHADOW_SET, 2);
outb(SHADOW_CNTL, 0);
outb(SHADOW_SET, 1);
outb(SHADOW_CNTL, 0);
outb(SHADOW_SET, set);
}
static void
LockShadowSet(void)
{
outb(SHADOW_SET, 1);
outb(SHADOW_CNTL, 0x3F);
outb(SHADOW_SET, 2);
outb(SHADOW_CNTL, 0x3F);
outb(SHADOW_SET, 0);
}
static void
ProgramShadowSet(int set, const ATI_CRTCSetup *setup)
{
UnlockShadowSet(set);
outb(DISP_CNTL, 0x53); /* Reset, blanking display. */
outb(H_TOTAL, setup->h_total);
outb(H_DISP, setup->h_disp);
outb(H_SYNC_START, setup->h_sync_start);
outb(H_SYNC_WIDTH, setup->h_sync_wid);
outw(V_TOTAL, setup->v_total);
outw(V_DISP, setup->v_disp);
outw(V_SYNC_START, setup->v_sync_start);
outb(V_SYNC_WID, setup->v_sync_wid);
outb(DISP_CNTL, setup->disp_cntl);
outw(CLOCK_SELECT, setup->clock_select);
/* Zero overscan registers to avoid weird borders. */
outw(HORIZONTAL_OVERSCAN, 0);
outw(VERTICAL_OVERSCAN, 0);
outb(OVERSCAN_COLOR_BLUE, 0);
outb(OVERSCAN_COLOR_GREEN, 0);
outb(OVERSCAN_COLOR_RED, 0);
LockShadowSet();
}
static void
SelectShadowSet(int set)
{
unsigned char v;
switch (set) {
case 0:
v = 2;
break;
case 1:
v = 3;
break;
case 2:
v = 7;
break;
default:
return;
}
outb(ADVFUNC_CNTL, v);
}
static void
SetMemOffset(void)
{
unsigned int vga_boundary;
/* Get the video memory boundary, in 256K pages. The 8514 controller
* cannot access memory below this, and the VGA controller cannot
* access memory above this. */
vga_boundary = inb(MEM_BNDRY) & 0x0f;
/* Set the start of the CRT buffer to match the start of the
* 8514 controller VRAM address. We also set up the graphics engine... */
outb(GE_OFFSET_HI, vga_boundary);
outb(CRT_OFFSET_HI, vga_boundary);
outb(GE_OFFSET_LO, 0);
outb(CRT_OFFSET_LO, 0);
}
/*
* Returns the physical base address of the memory aperature. The method
* used to decode this varies based on the BIOS and Mach32 chip type.
*/
static unsigned int
MemoryAperatureBaseAddress(void)
{
unsigned char *ip;
unsigned int location; /* Base address in Mbytes */
/*
* The PC BIOS is broken into 2 Kb slices. On startup, the ATI BIOS
* stashes away which 2 Kb slice it live in in scratch reg 0.
*/
ip = (unsigned char *)
(((inb(ROM_SCRATCH_PAD_0) & 0x7F) * 0x800) + ATI_BIOS_BASEADDR);
/* Look up the BIOS flag to see if it uses extended or simple format */
if ( (ip[0x62] & 1) != 0 ) /* Corsair/Nova style BIOS */
{
/*
* MEM_CFG+1 specifies an address from 0-127 Mb
* (MSB is always 0), and the scratch register specifies
* what the external hardware decodes, up to 4 Gb.
*/
location = ((inb(ROM_SCRATCH_PAD_0 + 1) & 0x1F) << 7)
| (inb(MEM_CFG + 1));
}
else /* No external address decode. Just use MEM_CFG register */
{
if ((inb(CONFIG_STATUS_2 + 1) & MEM_APERATURE_4GB_RANGE) != 0)
location = inw( MEM_CFG ) >> 4;
else
location = inb( MEM_CFG + 1 );
}
#if DEBUG
IOLog("MemoryAperatureBaseAddress() == 0x%x\n", location * ONE_MEG);
#endif
return (location * ONE_MEG); /* Return phys address in bytes, not Mb */
}
/*
* Returns the physical base address of the memory aperature. The method
* used to decode this varies based on the BIOS and Mach32 chip type.
*/
static BOOL
SetMemoryAperatureBaseAddress(unsigned int newLocation)
{
unsigned char *ip;
unsigned int location;
#if DEBUG
IOLog("SetMemoryAperatureBaseAddress(0x%x)\n", newLocation );
#endif
/* Convert location to Mbytes. Make sure it's on a 1 Mb boundry */
if ( (newLocation & (ONE_MEG - 1)) != 0 )
return NO;
location = newLocation / ONE_MEG;
/*
* The PC BIOS is broken into 2 Kb slices. On startup, the ATI BIOS
* stashes away which 2 Kb slice it live in in scratch reg 0.
*/
ip = (unsigned char *)
(((inb(ROM_SCRATCH_PAD_0) & 0x7F) * 0x800) + ATI_BIOS_BASEADDR);
/* Look up the BIOS flag to see if it uses extended or simple format */
if ( (ip[0x62] & 1) != 0 ) /* Corsair/Nova style BIOS */
{
/*
* It's never safe to reprogram these, as we don't know
* how to set the off-chip decoding logic.
*/
return NO;
}
else /* No external address decode. Just use MEM_CFG register */
{
if ((inb(CONFIG_STATUS_2 + 1) & MEM_APERATURE_4GB_RANGE) != 0)
{
outw(MEM_CFG, (location << 4)|(inw( MEM_CFG ) & 0xF));
}
else
{
if ( newLocation > ATI_LOCALBUS_VRAM_ADDRESS )
return NO; /* Invalid addr for this config */
outb( MEM_CFG + 1, location );
}
}
#if DEBUG
IOLog("SetMemoryAperatureBaseAddress(0x%x) succeeds\n", newLocation );
#endif
return YES;
}
/* Undo our munging of the DAC. Put DAC back in a state usable by VGA mode.
*/
static void
reset_DAC(void)
{
ATI_DAC dac_type;
/* Disable VGA passthrough mode so the DAC may be programmed. */
outw(CLOCK_SELECT, inw(CLOCK_SELECT) | 1);
dac_type = (inb(CONFIG_STATUS_1 + 1) >> 1) & 7;
switch(dac_type) {
case ATI_DAC_ATT20C491: /* ATT 20C491 */
case ATI_DAC_Bt481: /* Brooktree Bt481, Bt482 */
/* TO DO: Not tested yet */
outw(EXT_GE_CONFIG, 0x101A); /* DAC reg 1. */
outb(DAC_MASK, 0); /* VGA mode. */
break;
case ATI_DAC_68875: /* ATI or TI 34075. */
outw(EXT_GE_CONFIG, 0x201A); /* DAC reg 1. */
outb(INPUT_CLK_SEL, 0); /* clock 0. */
outb(OUTPUT_CLK_SEL, 0); /* SCLK & VCLK 1. */
outb(MUX_CNTL, 0x2D); /* MUX CNTL to 8/16. */
/* Set default 8 BPP delay and blank adjust. */
outw(LOCAL_CNTL, (inw(LOCAL_CNTL) | 0x8));
/* Set PIXEL DELAY = 3, BLANK ADJ = 0 (0xC). */
outb(MISC_CNTL + 1, (inb(R_MISC_CNTL + 1) & 0xF0) | 0xC);
/* Set horizontal skew. */
outw(HORIZONTAL_OVERSCAN, 1);
break;
case ATI_DAC_68830: /* ATI 68830*/
default:
/* Set PIXEL DELAY = 0, BLANK ADJ = 0. */
outb(MISC_CNTL + 1, (inb(R_MISC_CNTL + 1) & 0xF0) | 0);
/* Set horizontal skew. */
outw(HORIZONTAL_OVERSCAN, 0);
break;
}
/* Put DAC in 6 bit mode, engine in 8 bit mode. */
outw(EXT_GE_CONFIG, 0x001A); /* reset EXT_DAC_ADDR */
/* Enable VGA passthrough mode. */
outw(CLOCK_SELECT, inw(CLOCK_SELECT) & ~1);
}
static void
program_TI34075(const ATI_DACSetup *parm, const IODisplayInfo *displayMode)
{
ATI_CRTCSetup *setup;
/* DAC RS 3 active, 8 bit pixel width. */
outw(EXT_GE_CONFIG, 0x201A);
outb(OUTPUT_CLK_SEL, parm->out_clk);
outb(MUX_CNTL, parm->mux);
outb(INPUT_CLK_SEL, parm->in_clk);
outw(EXT_GE_CONFIG, parm->GE_config);
outb(DAC_MASK, parm->mask);
/* Set blank adjust and pixel delay values per parm->delay_timing. */
outb(MISC_CNTL + 1, (inb(R_MISC_CNTL + 1) & 0xf0) | parm->delay_timing);
setup = (ATI_CRTCSetup *)displayMode->parameters;
if (setup->mux_flag == 0)
return;
/* Set PIXEL DELAY = 0, BLANK ADJ = 1. */
outb(MISC_CNTL + 1, (inb(R_MISC_CNTL + 1) & 0xF0) | 1);
UnlockShadowSet(2);
outw(CLOCK_SELECT, setup->clock_select);
LockShadowSet();
SelectShadowSet(2);
}
static void
program_ATI68830(const ATI_DACSetup *parm, const IODisplayInfo *displayMode)
{
/* Set pixel depth, packing, aliased monitor ID of 2. */
outw(EXT_GE_CONFIG, parm->GE_config & 0x0fff);
}
/* Brooktree Bt481, Bt482 or ATT 20C491 RAMDAC setup code.
* On entry, the CRT controller is programmed, and VGA passthru is disabled.
* TO DO: Test with ATT 20C491.
*/
static void
program_Bt481(const ATI_DACSetup *parm, const IODisplayInfo *displayMode)
{
/* The clock should already be set appropriately for a 'slow', or
* double-clocked DAC on entry. */
/* Activate the TrueColor mode in the DAC. */
/* DAC RS 2 active, 8 bit pixel width. */
outw(EXT_GE_CONFIG, 0x101A);
/* Set reg 6 to correct mode. */
outb(DAC_MASK, parm->mode);
/* Set blank adjust and pixel delay values. */
outb(MISC_CNTL + 1, (inb(R_MISC_CNTL + 1) & 0xf0) | parm->delay_timing);
/* Set pixel depth, packing, aliased monitor ID of 2. */
outw(EXT_GE_CONFIG, parm->GE_config & 0x0fff);
/* Set the appropriate DAC pixel mask. */
outb(DAC_MASK, parm->mask);
}
/* Set up a simple gamma corrected grayscale. We rely on the Window Server
* invoking this function once via IO_SET_TRANSFER_TABLE during startup.
*/
static void
ATISetTransferTable(const unsigned int *table)
{
ATI_DAC dac_type;
int g, val;
unsigned int scale;
dac_type = (inb(CONFIG_STATUS_1 + 1) >> 1) & 7;
switch (dac_type) {
case ATI_DAC_68875: /* ATI or TI 34075 */
case ATI_DAC_68830: /* ATI 68830*/
case ATI_DAC_Bt476:
scale = 0;
break;
case ATI_DAC_ATT20C491: /* ATT 20C491 */
case ATI_DAC_Bt481: /* Brooktree Bt481, Bt482 */
default:
scale = 2;
break;
}
outb(DAC_W_INDEX, 0);
for (g = 0; g < IO_8BPP_TRANSFER_TABLE_SIZE; g++) {
val = (table[g] & 0x000000FF);
if (scale)
val >>= scale;
outb(DAC_DATA, val);
outb(DAC_DATA, val);
outb(DAC_DATA, val);
}
}
- (void)setupHardware
{
ATI_DAC dac_type;
const ATI_DACSetup *parm;
SetMemOffset(); /* Set VGA VRAM memory offset. */
dac_type = (inb(CONFIG_STATUS_1 + 1) >> 1) & 7;
/* Load a register shadow set with our desired setup. */
/* Load CRT regs, and enable controller in appropriate mode, unblanking
* the display in the process. */
ProgramShadowSet(2, CRTControllerSetup);
/* Select our setup, re-enabling the CRT controller. */
SelectShadowSet(2);
/* At this point, the CRT timing is set, VGA passthrough is disabled,
* and the pixel clock is set. We need to place the DAC in the correct
* mode of operation. */
if (displayMode->bitsPerPixel == IO_15BitsPerPixel)
parm = &ATI_DAC_Setup_15BPP;
else
parm = &ATI_DAC_Setup_8BPP;
switch (dac_type) {
case ATI_DAC_68830: /* ATI 68830*/
program_ATI68830(parm, displayMode);
outw(HORZ_OVERSCAN, 1);
break;
case ATI_DAC_ATT20C491: /* ATT 20C491 */
case ATI_DAC_Bt481: /* Brooktree Bt481, Bt482 */
program_Bt481(parm, displayMode);
break;
case ATI_DAC_68875: /* ATI or TI 34075 */
program_TI34075(parm, displayMode);
break;
default:
/* Bt476 or some other unusable part. */
IOLog("%s: Unknown DAC on ATI board - ID:%d\n", [self name], dac_type);
break;
}
/* Set the CRT pitch, in units of (pixels_per_line/8).
* CAUTION: Writing ADVFUNC_CNTL or MEM_CNTL (0xbee8) resets
* this to 1024 pixels/line. */
outb(CRT_LINE_PITCH, CRTControllerSetup->xres >> 3);
/* Also set the graphics engine... */
outb(GE_PITCH, CRTControllerSetup->xres >> 3);
}
- (vm_offset_t)prepareMemoryAperture
{
unsigned int physLoc;
vm_offset_t virtLoc;
vm_size_t length, needed_length;
ATI_DAC dac_type;
int mode, k;
BOOL valid[ATIModeCount];
BOOL slowDAC = NO;
IORange *range;
const IODisplayInfo *displayTable;
int numTableEntries;
const IODisplayInfo *displayDefault;
/* Set memory boundary to share. */
outb(MEM_BNDRY, 0);
/* Enable memory aperture with a 4 Mb width, page sel for page 0. */
outb(MEM_CFG, (inb(MEM_CFG) & 0xf0) | 0x02);
/* Get memory aperture location (units in Mbyte). This is hardware
* and version dependent. The high 8 bits of MEM_CFG specify the
* memory aperature location between 0 and 127Mb, in 1 Mb units.
*/
range = [[self deviceDescription] memoryRangeList];
if (range == NULL) {
IOLog("%s: No memory range set.\n", [self name]);
return 0;
}
physLoc = 0; /* Default to Configure app settings. */
/* Use the Misc Options register (36EE) bits 2&3 to determine
* the amount of installed memory. 0 = 512, 1 = 1M, 2 = 2M, 3 = 4M.
* Select a timing setup based on available memory and DACs.
* We do this here instead of in goLinear so we can report the
* potential frame buffer size early in the startup sequence.
*/
dac_type = (inb(CONFIG_STATUS_1 + 1) >> 1) & 7;
switch (dac_type) {
case ATI_DAC_68830: /* ATI 68830*/
case ATI_DAC_68875: /* ATI or TI 34075 */
slowDAC = NO;
break;
case ATI_DAC_ATT20C491: /* ATT 20C491 */
case ATI_DAC_Bt476: /* Brooktree Bt476, Bt478 */
case ATI_DAC_Bt481: /* Brooktree Bt481, Bt482 */
default:
/* These DACs can't handle the clock rates at 1024x768. */
slowDAC = YES;
break;
}
switch ((inb(MISC_OPTIONS) & 0x0c) >> 2) {
default:
case 0: /* 512 Kb VRAM. */
return (vm_offset_t)0;
case 1: /* 1 Mb VRAM. */
length = ONE_MEG;
break;
case 2: /* 2 Mb VRAM. */
case 3: /* 4 Mb VRAM. */
length = 2 * ONE_MEG; /* The most we currently need. */
break;
}
switch (ati_flavor) {
case ATI_EISA_Card: /* Definitely programmable address. */
case ATILocalBusCard: /* Usually a programmable address. */
/* Read out the full address using the mechanism endorsed by
* ATI. This mechanism is supported by ATI in their Windows
* driver, and every vendor we've tested follows it rather than
* rolling their own drivers.
*/
physLoc = MemoryAperatureBaseAddress();
/* If the address is in a range supported by the Mach32
* chipset (below 128 Mb), try to program it. Otherwise,
* we'll assume the address is hardwired, and pick it up
* using the ATI endorsed addressing mechanism. */
if ( physLoc != range->start )
SetMemoryAperatureBaseAddress( range->start );
/* FALL THRU */
default: /* Unknown device. */
case ATI_NovaCard: /* Not programmable in prototypes tested. */
case ATICorsair: /* Not programmable. */
/* Read out the full address using the mechanism endorsed by
* ATI. This mechanism is supported by ATI in their Windows
* driver, and every vendor we've tested follows it rather than
* rolling their own drivers.
*/
physLoc = MemoryAperatureBaseAddress();
/* If the address doesn't match the one set in Configure, complain.
* We then override the Configure address with the address reported
* by the hardware. This gives the driver a fighting chance of
* working correctly, and works around a bug in 3.1 Configure and
* DriverKit which prevents setting addresses in the high 2 Gb of
* address space.
*/
if (range->start != physLoc || range->size < length) {
IOLog("%s: FB addr decodes as 0x%x, overriding configured "
"addr 0x%x.\n", [self name], physLoc, range->start);
/* Force the range to match the hardware config,
* so the Window Server will work correctly. */
range->start = physLoc;
range->size = length;
/* Reprogram the device description with usable values. */
[[self deviceDescription] setMemoryRangeList:range
num:[[self deviceDescription] numMemoryRanges]];
}
break;
}
/* Enable kernel access with the appropriate mapping. */
virtLoc = [self mapFrameBufferAtPhysicalAddress:physLoc length:length];
if (virtLoc == 0)
return virtLoc;
/* If the DAC is a slow double-clocked part (ATT20C491, Bt481) use
* the special setup for the part. */
if (slowDAC == YES) {
displayTable = ATISlowDACMode;
numTableEntries = ATISlowDACModeCount;
displayDefault = &ATISlowDACMode[ATI_DEFAULT_SlowDAC_MODE];
} else {
displayTable = ATIMode;
numTableEntries = ATIModeCount;
displayDefault = (length > ONE_MEG) ? &ATIMode[ATI_DEFAULT_2MEG_MODE]
: &ATIMode[ATI_DEFAULT_1MEG_MODE];
}
/* Determine which entries are valid for the amount of memory in the
* system. */
for (k = 0; k < numTableEntries; k++) {
needed_length = displayTable[k].width * displayTable[k].height;
if (displayTable[k].bitsPerPixel != IO_8BitsPerPixel)
needed_length *= sizeof(short);
valid[k] = (needed_length <= length);
/* The 68830 DAC can't handle higher res than 1024x768. */
if ( dac_type == ATI_DAC_68830 && displayTable[k].width > 1024 )
valid[k] = NO;
}
/* Look up the correct entry to use. */
mode = [self selectMode:displayTable count:numTableEntries valid:valid];
if (mode < 0)
displayMode = displayDefault;
else
displayMode = &displayTable[mode];
CRTControllerSetup = (ATI_CRTCSetup *)displayMode->parameters;
return virtLoc;
}
- (void)establishLinearFB
{
/* Local Bus timing magic */
if ( inw( MACH32_STEP6_ID_REG ) != MACH32_STEP6_ID_VALUE )
{
asm volatile(
"pushl %eax; \n"
"pushl %edx; \n"
"; \n"
"movl 0x01CE, %edx; \n"
"movl 0xAE, %eax; \n"
"outb %al, %dx; \n"
"incl %edx; \n"
"inb %dx, %al; \n"
"andb $ ~0x10, %al; \n"
"movb %al, %ah; \n"
"movb $0xAE, %al; \n"
"decl %edx; \n"
"outw %ax, %dx; \n"
"; \n"
"movl $0x2185,%eax; \n"
"outw %ax,%dx; \n"
"; \n"
"movl $0x01CE, %edx; \n"
"movl $0xAE, %eax; \n"
"outb %al, %dx; \n"
"incl %edx; \n"
"inb %dx, %al; \n"
"orb $ 0x10, %al; \n"
"movb %al, %ah; \n"
"movl $0xAE, %al; \n"
"decl %edx; \n"
"outw %ax, %dx; \n"
"; \n"
"popl %edx; \n"
"popl %eax; \n");
};
/* Program controller to 16bpp or 8bpp mode. */
[self setupHardware];
}
/* Determine whether there is an ATi chipset present.
* 1) Check for an EISA card. If found, we're done.
* 2) Check for EISA CPU board ID. If it's a Corsair, we're done.
* 3) Fallback: assume a localbus+ISA card. (No ID mechanism!)
*/
- (boolean_t)ATIPresent
{
int slot;
unsigned int product_id;
/* First, scan for a board in an EISA slot. */
if ([self isEISAPresent] == TRUE) {
for (slot = 1; slot < 16; slot++) {
if ([self getEISAId:&product_id forSlot:slot] == TRUE) {
switch (product_id & ~0x0f) {
case ATI_EISA_ID:
ati_flavor = ATI_EISA_Card;
return TRUE;
case ATI_NOVA_PBUS_EISA_ID:
ati_flavor = ATI_NovaCard;
return TRUE;
}
}
}
/* Examine the EISA ID for the motherboard, found in 'slot 0'.
* Do this AFTER the slot scan, so we preferentially use boards
* in the EISA slots.
*/
if ([self getEISAId:&product_id forSlot:0] == TRUE
&& (product_id & ~0x0f) == INTEL_CORSAIR_ID) {
ati_flavor = ATICorsair;
return TRUE;
}
}
IOLog("%s: Assuming a local bus ATI card.\n", [self name]);
ati_flavor = ATILocalBusCard;
return TRUE;
}
- (void)enterLinearMode
{
[self establishLinearFB];
}
- initFromDeviceDescription:deviceDescription
{
void *frameBuffer;
IODisplayInfo *display;
if ([super initFromDeviceDescription:deviceDescription] == nil)
return [super free];
if (![self ATIPresent])
return [super free];
display = [self displayInfo];
frameBuffer = (void *)[self prepareMemoryAperture];
if (frameBuffer == 0)
return nil;
*display = *displayMode;
display->frameBuffer = frameBuffer;
display->flags = IO_DISPLAY_CACHE_WRITETHROUGH;
if (displayMode->bitsPerPixel == IO_15BitsPerPixel) {
display->flags |= IO_DISPLAY_NEEDS_SOFTWARE_GAMMA_CORRECTION;
} else if (displayMode->bitsPerPixel == IO_8BitsPerPixel) {
display->flags |= IO_DISPLAY_HAS_TRANSFER_TABLE;
}
return self;
}
- free
{
return [super free];
}
- (void)revertToVGAMode
{
/* Select VGA setup, re-enabling the VGA CRT controller. */
SelectShadowSet(0); /* Select VGA CRT configuration. */
reset_DAC(); /* Restore DAC for VGA operation. */
[super revertToVGAMode]; /* Let superclass do generic VGA stuff. */
}
- setTransferTable:(const unsigned int *)table count:(int)count
{
if (displayMode->bitsPerPixel == IO_8BitsPerPixel)
ATISetTransferTable(table);
return self;
}
@end