diff options
author | Kaleb Keithley <kaleb@freedesktop.org> | 2003-11-14 16:48:55 +0000 |
---|---|---|
committer | Kaleb Keithley <kaleb@freedesktop.org> | 2003-11-14 16:48:55 +0000 |
commit | f8465308152c64c6e05ebf814794aec1022046c5 (patch) | |
tree | 29ed6714ef5efd83b989cc4f5a9e3a239cb144ac |
Initial revisionXORG-STABLE
-rw-r--r-- | man/tseng.man | 30 | ||||
-rw-r--r-- | src/README | 264 | ||||
-rw-r--r-- | src/tseng.h | 391 | ||||
-rw-r--r-- | src/tseng_accel.c | 936 | ||||
-rw-r--r-- | src/tseng_acl.c | 232 | ||||
-rw-r--r-- | src/tseng_acl.h | 232 | ||||
-rw-r--r-- | src/tseng_bank.c | 87 | ||||
-rw-r--r-- | src/tseng_clock.c | 508 | ||||
-rw-r--r-- | src/tseng_colexp.c | 543 | ||||
-rw-r--r-- | src/tseng_cursor.c | 270 | ||||
-rw-r--r-- | src/tseng_dga.c | 249 | ||||
-rw-r--r-- | src/tseng_dpms.c | 249 | ||||
-rw-r--r-- | src/tseng_driver.c | 3292 | ||||
-rw-r--r-- | src/tseng_inline.h | 228 | ||||
-rw-r--r-- | src/tseng_ramdac.c | 667 |
15 files changed, 8178 insertions, 0 deletions
diff --git a/man/tseng.man b/man/tseng.man new file mode 100644 index 0000000..e410207 --- /dev/null +++ b/man/tseng.man @@ -0,0 +1,30 @@ +.\" $XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/tseng.man,v 1.2 2001/01/27 18:20:55 dawes Exp $ +.\" shorthand for double quote that works everywhere. +.ds q \N'34' +.TH TSENG __drivermansuffix__ __vendorversion__ +.SH NAME +tseng \- Tseng Labs video driver +.SH SYNOPSIS +.nf +.B "Section \*qDevice\*q" +.BI " Identifier \*q" devname \*q +.B " Driver \*qtseng\*q" +\ \ ... +.B EndSection +.fi +.SH DESCRIPTION +.B tseng +is an XFree86 driver for Tseng Labs video cards. +THIS MAN PAGE NEEDS TO BE FILLED IN. +.SH SUPPORTED HARDWARE +The +.B tseng +driver supports... +.SH CONFIGURATION DETAILS +Please refer to XF86Config(__filemansuffix__) for general configuration +details. This section only covers configuration details specific to this +driver. +.SH "SEE ALSO" +XFree86(1), XF86Config(__filemansuffix__), xf86config(1), Xserver(1), X(__miscmansuffix__) +.SH AUTHORS +Authors include: ... diff --git a/src/README b/src/README new file mode 100644 index 0000000..dca1c6b --- /dev/null +++ b/src/README @@ -0,0 +1,264 @@ + + +This file is NOT up to date for the New Design! + + + + +============== old (pre-ND) contents below ============== + +"I just thought it would be usefull if we had some kind of TODO and BUGS +files in the distribution as it would make it easier to see what is needed +to be done and what could be done better, instead of browsing through the +sourcecode. And we whould be able to se the progress literally by the ever +decreasing TODO file :-)" + + +## BUGS: + +All Tseng cards: + +* We definitely NEED to fix that color-expansion problem. See Appendix A +below for a detailed explanation. + +* There are still some problems with the HW-cursor. The error message about +"wrong color selected" is disabled, and the limitation documented. Better +would be to have a way to dynamically switch to software-cursor mode if the +color can not be made. HW cursor doesn't work in DoubleScan modes yet (only +half of the cursor displayed) + +* text font sometimes corrupted when going back to text mode. This may be +related to the order in which registers are restored: the ARK driver first +restores extended registers before restoring the standard registers for +excactly this reason. + +* The code needs to be heavily reworked to fix all sorts of data type +problems. The current code will certainly not run on an Alpha. The first +step is to replace all hardware related variables by CARD8/CARD16/CARD32 +types. + + +ET6000: + +* The trapezoid code is disabled because it doesn't comply with the way the +non-accelerated ("cfb") code does things. This needs to be fixed. + + +ET-4000(W32): + +* Hardware cursor support for the W32 is still lacking color support. We +need to reserve color cells #0 and #255 to make this work. From discussions +on the development list, it seems the best solution is to allocate these cells +read-write, and then use them for the HW cursor. We MUST however document +that this will break some clients which depend on a fixed color in cell #0, +and some others that rely on the presence of 256 color cells. It will also +cause cursor color problems when someone uses a local color map. + + +## TODO: + +All cards: + +* The accelerator on the Tseng devices is capable of much more. Especially +the pattern support is not used most of the time: It can render a pattern in +just about every accelerated operation. This means patterned lines, bitblts, +screencopies, etc. are possible. However, operations like these are very +uncommon in normal server use, so the speed benefit would go largely unnoticed. + + +ET4000: + +* support needs to be added for several clockchips and RAMDACs: + - 8-bit RAMDAC support for >8bpp modes: Sierra DACs and possibly others + - AT&T 20C49x RAMDAC support is not correct. + +* SuperProbe could use an update. It doesn't detect some of the RAMDACs that +are detected by the driver. + +* Several of the color expansion-related accelerations are still only 8bpp. +It should be easy to use the same trick on those as on the standard color +expand code (use intermediate buffer, expand data before blitting). + +* many of the operations that the W32 family can't support natively (e.g. +FillRectSolid for 24bpp) can be performed using CPU-to-screen operations, +feeding the correct (color) information through the ACL aperture. + + +ET6000: + +* someone might want to look at how the bitBLT engine of the ET6000 is +constructed, and come up with some fancy ways of abusing it. We're still +only using a small part of it (I'm thinking about the compare map and the +extensions to the MIX hardware compared to the ET4000). + +* Mclk support is still lacking (that would also allow MClk-dependent +maximum bandwidth). + +* Apart from the things mentionned above, I think the ET6000 server is +pretty complete. Some optimisations could possibly be added. Like for +example some assembler code for calculating a framebuffer address from X/Y +coordinates. That would help to speed up small blits. + + +======================================================================= +APPENDIX A: the color expansion problem +---------------------------------------- + +As suggested in the data book, we're doing font rendering using the +color-expansion (MIX map) capabilities of the Tseng accelerator. + +We're using a ping-pong buffer scheme (triple buffering actually) in +off-screen memory to store one scanline worth of font data at a time. each +of these scanlines is "blitted" to on-screen memory using the accelerator. +The scanline is the MIX map, and there's also a 4x1 solid foreground color +(SRC map), and a 4x1 solid background color (PAT map). + +Basically, the flow is as follows: + + - setup accelerator for font-expansion + + - store scanline 1 in off-screen memory buffer 1 + + - start operation + + - store scanline 2 in off-screen memory buffer 2 + + - start operation + + - store scanline 3 in off-screen memory buffer 3 + + - start operation + + - store scanline 4 in off-screen memory buffer 1 + + - start operation + + ... etc, until the whole line of text is drawn. + +There is no explicit "waiting" for the accelerator to finish an operation +before starting a new one, because it has been set up to add "wait-states" +when the queue is full. We're aiming to use concurrency between the +accelerator and the storing of scanlines in the buffers. Anyway, waiting +after each operation doesn't help. + +Now, in 99% of all cases, text is rendered OK. But in some cases, we're +seeing severe font corruption. + +What we're seeing is this: sometimes, exactly 32 pixels of a scanline are +rendered with the scanline data that was there BEFORE, instead of the one +that was just written into the scanline buffer. In other words, 32 pixels of +line 2 (for example) are rendered at line 5. The rest of the scanline can be +OK (i.e. data from scanline 5 is actually written there). + +Here's an attempt at showing you what _should_ have been rendered: + +1 +2 ##################################################################### +3 +4 +5 +6 ##################################################################### +7 +8 +9 +10 ##################################################################### +11 +12 +13 +14 ##################################################################### +15 + + + +and what _is_ rendered sometimes (only an example): + +1 +2 ##################################################################### +3 +4 +5 +6 ######################## ############# +7 +8 +9 +10 ##################################################################### +11 +12 +13 ######################## +14 ##################################################################### +15 + +At line 6, 32 pixels of the "black" scanline data from line 3 is rendered +instead of the actual full-white that would normally have to be there. At +line 13, the opposite happened (data from line 10 rendered at line 13). This +32-pixel width of the "bug" is independent of the color depth: we're seeing +this at 8bpp as well as at 16bpp, 24bpp and 32bpp. 32 pixels each time. + +Remember, we're talking triple-buffering here, so the "wrongly" rendered +data is in fact the data that was in the scanline-buffer from the PREVIOUS +operation that used that buffer. + +In fact, my best explanation is that sometimes, a whole DWORD (32 bits) of +data isn't in the video memory yet by the time the accelerator starts +rendering with it. + +But the data _is_ being written to there by the driver software, because if +you restart the scanline-operation again, without writing any more data to +the scanline buffers (only the MIX address and the destination address are +reprogrammed to restart the scanline color expansion operation -- see code +in tseng_acl.c), data _is_ rendered correctly. + + + +I have investigated this as far as I possibly can. I checked if the data was +actually written in video memory. It was. I checked all kinds of PCI-related +things, like write-gathering or write-reordering of the PCI chipset, etc. I +disabled all possible enhanced features, both on the PCI chipset, inside the +CPU, and on the ET6000. + +What strikes me, is that the exact same problems are seen on ET4000W32p as +on the ET6000. This immediately rules out any special features that were +only added with the ET6000, like problems with the MDRAM cache buffers, etc. +It seems to be a generic problem to all Tseng accelerators. + +The exact same higher-level code is being used for other chipsets as well +(i.e. the system of writing scanlines of data to off-screen memory and +making the accelerator expand it into on-screen memory), and there are no +problems on these other chipsets. The acceleration architecture we're using +is completely device-independent up to the point where each chip needs to +provide a + + SetupForScanlineScreenToScreenColorExpand() + +and a + + SubsequentScanlineScreenToScreenColorExpand() +function. + +Since the higher-level code is being used by other chip drivers as well, it +seems to be OK. + +So the problem is either in those device-dependent functions, or in the +hardware itself. + + +I have found one kludge to work around this problem, and it should (?) tell +you a lot about the problem: if I start each scanline-colorexpand operation +TWICE, rendering is suddenly perfect (at least there are so little rendering +errors that I haven't seen any yet). + + +I am including the two device-depending functions so that you may be able to +follow what I'm saying here: + + + +One entire line of text is drawn by calling the Setup() function ONCE. All +scanlines of text (16 of them in case of a 8x16 font) are drawn by filling +the off-screen scanline buffers and calling the Subsequent() function. + + + + + +$XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/README,v 1.12 2000/08/08 08:58:06 eich Exp $ diff --git a/src/tseng.h b/src/tseng.h new file mode 100644 index 0000000..926f813 --- /dev/null +++ b/src/tseng.h @@ -0,0 +1,391 @@ + +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/tseng.h,v 1.37 2002/04/04 14:05:49 eich Exp $ */ + + + + + +#ifndef _TSENG_H +#define _TSENG_H + +/* All drivers should typically include these */ +#include "xf86.h" +#include "xf86_OSproc.h" + +/* All drivers need this */ +#include "xf86_ansic.h" + +/* Everything using inb/outb, etc needs "compiler.h" */ +#include "compiler.h" + +/* This is used for module versioning */ +#include "xf86Version.h" + +/* Drivers for PCI hardware need this */ +#include "xf86PciInfo.h" + +/* Drivers that need to access the PCI config space directly need this */ +#include "xf86Pci.h" + +/* All drivers using the vgahw module need this */ +/* All Tseng chips _need_ VGA register access, so multihead operation is out of the question */ +#include "vgaHW.h" + +/* Drivers using the mi banking wrapper need this */ +#include "mibank.h" + +/* All drivers using the mi colormap manipulation need this */ +#include "micmap.h" + +/* Needed for the 1 and 4 bpp framebuffers */ +#include "xf1bpp.h" +#include "xf4bpp.h" +#include "fb.h" + +/* Drivers using the XAA interface ... */ +#include "xaa.h" +#include "xaalocal.h" +#include "xf86Cursor.h" +#include "xf86fbman.h" + + +/* functions in tseng_driver.c needed outside it */ +void TsengBlankScreen(ScrnInfoPtr pScrn, Bool unblank); +void TsengProtect(ScrnInfoPtr pScrn, Bool on); + + +#define MAX_TSENG_CLOCK 86000 /* default max clock for standard boards */ + +/* + * Contrary to the old driver, we use the "Chip Revision" here intead of + * multiple chipsets like "TYPE_ET4000W32Pa", "TYPE_ET4000W32Pb", etc. + */ + +typedef enum { + TYPE_ET4000, + TYPE_ET4000W32, + TYPE_ET4000W32I, + TYPE_ET4000W32P, + TYPE_ET6000, + TYPE_ET6100, + TYPE_TSENG +} t_tseng_type; + +/* revision ID for W32 chips: currently used for W32i and W32p */ +typedef enum { + TSENGNOREV = 0, + W32REVID_A, + W32REVID_B, + W32REVID_C, + W32REVID_D +} t_w32_revid; + +#define ET6100REVID (0x70) + +typedef enum { + T_BUS_ISA, + T_BUS_MCA, + T_BUS_VLB, + T_BUS_PCI +} t_tseng_bus; + +extern SymTabRec TsengDacTable[]; + +typedef enum { + UNKNOWN_DAC = -1, + NORMAL_DAC, + ATT20C47xA_DAC, + Sierra1502X_DAC, + ATT20C497_DAC, + ATT20C490_DAC, + ATT20C493_DAC, + ATT20C491_DAC, + ATT20C492_DAC, + ICS5341_DAC, + ICS5301_DAC, + STG1700_DAC, + STG1702_DAC, + STG1703_DAC, + ET6000_DAC, + CH8398_DAC, + MUSIC4910_DAC +} t_ramdactype; + +typedef enum { + CLOCKCHIP_ICD2061A, + CLOCKCHIP_ET6000, + CLOCKCHIP_ICS5341, + CLOCKCHIP_ICS5301, + CLOCKCHIP_CH8398, + CLOCKCHIP_STG1703 +} t_clockchip_type; + +typedef enum { + TSENG_MODE_NORMAL, + TSENG_MODE_PIXMUX, + TSENG_MODE_DACBUS16 +} t_clockrange_type; + +typedef struct { + unsigned char cmd_reg; + unsigned char f2_M; + unsigned char f2_N; + unsigned char ctrl; + unsigned char w_idx; + unsigned char r_idx; + unsigned char timingctrl; /* for STG170x */ + unsigned char MClkM; + unsigned char dummy; /* FIXME!!! : someone overwrites saved MClkN without this */ + unsigned char MClkN; /* PLL M/N values for MemClk programming */ +} PllState; + +typedef struct { + unsigned char ExtCRTC[16]; /* CRTC 0x30 .. 0x3F */ + unsigned char ExtTS[2]; /* TS 0x06 .. 0x07 */ + unsigned char ExtATC; /* ATC 0x16 */ + unsigned char ExtSegSel[2]; /* 0x3CD , 0x3CB */ + unsigned char ExtET6K[0x4F]; /* ET6000 PCI config space registers 0x40 .. 0x8F */ + unsigned char ExtIMACtrl; /* IMA port control register (0x217B index 0xF7) */ + PllState pll; /* registers in GenDAC-like RAMDAC/clockchips */ + unsigned char ATTdac_cmd; /* command register for ATT 49x DACs */ +} TsengRegRec, *TsengRegPtr; + +typedef struct { + unsigned char save1, save2, save3, save4; +} clock_save; + +typedef struct { + Bool Programmable; /* MemClk is programmable if set */ + Bool Set; /* reprogram MClk if TRUE */ + int MemClk; /* MemClk value in kHz */ + int min, max; /* MemClk limits */ +} TsengMClkInfoRec, *TsengMclkInfoPtr; + +typedef struct { + int saved_cr; + int rmr; +} dac_save; + +typedef struct { + t_ramdactype DacType; + Bool NotAttCompat; /* avoid treating the RAMDAC as AT&T compatible */ + int RamdacShift; /* typically 10 or 8 for 6- or 8-bit dac */ + int RamdacMask; /* typically 0x3f for 6 bit, 0xff for 8-bit ramdac */ + Bool Dac8Bit; /* dac is 8 bit instead of the default 6 bit */ + Bool DacPort16; /* Ramdac port is 16 bits wide instead of default 8 */ + rgb rgb24packed; +} TsengDacInfoRec, *TsengDacInfoPtr; + +typedef struct { + /* we'll put variables that we want to access _fast_ at the beginning (just a hunch) */ + unsigned char cache_SegSelL, cache_SegSelH; /* for tseng_bank.c */ + int Bytesperpixel; /* a shorthand for the XAA code */ + Bool need_wait_acl; /* always need a full "WAIT" for ACL finish */ + int line_width; /* framebuffer width in bytes per scanline */ + int planemask_mask; /* mask for active bits in planemask */ + int neg_x_pixel_offset; + int powerPerPixel; /* power-of-2 version of bytesperpixel */ + unsigned char *BresenhamTable; + /* normal stuff starts here */ + pciVideoPtr PciInfo; + PCITAG PciTag; + int Save_Divide; + Bool UsePCIRetry; /* Do we use PCI-retry or busy-waiting */ + Bool UseAccel; /* Do we use the XAA acceleration architecture */ + Bool HWCursor; /* Do we use the hardware cursor (if supported) */ + Bool Linmem_1meg; /* Is this a card limited to 1Mb of linear memory */ + Bool UseLinMem; + Bool SlowDram; + Bool FastDram; + Bool MedDram; + Bool SetPCIBurst; + Bool PCIBurst; + Bool SetW32Interleave; + Bool W32Interleave; + Bool ShowCache; + Bool Legend; /* Sigma Legend clock select method */ + Bool NoClockchip; /* disable clockchip programming clockchip (=use set-of-clocks) */ + TsengRegRec SavedReg; /* saved Tseng registers at server start */ + TsengRegRec ModeReg; + unsigned long icd2061_dwv; /* To hold the clock data between Init and Restore */ + t_tseng_bus Bustype; /* W32 bus type (currently used for lin mem on W32i) */ + t_tseng_type ChipType; /* "Chipset" causes confusion with pScrn->chipset */ + int ChipRev; + memType LinFbAddress; + unsigned char *FbBase; + memType LinFbAddressMask; + long FbMapSize; + miBankInfoRec BankInfo; + CARD32 IOAddress; /* PCI config space base address for ET6000 */ + CARD32 MMIOBase; + int MinClock; + int MaxClock; + int MemClk; + ClockRangePtr clockRange[2]; + TsengDacInfoRec DacInfo; + TsengMClkInfoRec MClkInfo; + t_clockchip_type ClockChip; + int max_vco_freq; /* max internal VCO frequency */ + CloseScreenProcPtr CloseScreen; + int save_divide; + XAAInfoRecPtr AccelInfoRec; + xf86CursorInfoPtr CursorInfoRec; + CARD32 AccelColorBufferOffset; /* offset in video memory where FG and BG colors will be stored */ + CARD32 AccelColorExpandBufferOffsets[3]; /* offset in video memory for ColorExpand buffers */ + unsigned char * XAAColorExpandBuffers[3]; /* pointers to colorexpand buffers */ + CARD32 AccelImageWriteBufferOffsets[2]; /* offset in video memory for ImageWrite Buffers */ + unsigned char * XAAScanlineImageWriteBuffers[2]; /* pointers to ImageWrite Buffers */ + CARD32 HWCursorBufferOffset; + unsigned char *HWCursorBuffer; + unsigned char * XAAScanlineColorExpandBuffers[1]; + int acl_blitxdir; + int acl_blitydir; + CARD32 acl_iw_dest; + CARD32 acl_skipleft; + CARD32 acl_ColorExpandDst; + int acl_colexp_width_dwords; + int acl_colexp_width_bytes; + dac_save dac; + CARD32* ColExpLUT; + clock_save save_clock; + EntityInfoPtr pEnt; + char * MMioBase; + pointer scratchMemBase; + pointer tsengCPU2ACLBase; + /* These will hold the ping-pong registers. */ + int tsengFg; + int tsengBg; + int tsengPat; + int tseng_old_dir; + int old_x; + int old_y; + int DGAnumModes; + Bool DGAactive; + DGAModePtr DGAModes; + int DGAViewportStatus; + OptionInfoPtr Options; +} TsengRec, *TsengPtr; + +#define TsengPTR(p) ((TsengPtr)((p)->driverPrivate)) + +#define Is_stdET4K ( pTseng->ChipType == TYPE_ET4000 ) +#define Is_W32 ( pTseng->ChipType == TYPE_ET4000W32 ) +#define Is_W32i ( pTseng->ChipType == TYPE_ET4000W32I ) +#define Is_W32p ( pTseng->ChipType == TYPE_ET4000W32P) +#define Is_ET6000 ( pTseng->ChipType == TYPE_ET6000 ) +#define Is_ET6100 ( pTseng->ChipType == TYPE_ET6100 ) + +#define Is_W32_W32i ( Is_W32 || Is_W32i ) +#define Is_W32_any ( Is_W32 || Is_W32i || Is_W32p ) +#define Is_W32p_ab ( Is_W32p && ( (pTseng->ChipRev == W32REVID_A) || (pTseng->ChipRev == W32REVID_B) ) ) +#define Is_W32p_cd ( Is_W32p && ( (pTseng->ChipRev == W32REVID_C) || (pTseng->ChipRev == W32REVID_D) ) ) +#define Is_ET6K ( Is_ET6000 || Is_ET6100 ) + +#define CHIP_SUPPORTS_LINEAR ( Is_W32i || Is_W32p || Is_ET6K ) + +#define DAC_IS_ATT49x ( (pTseng->DacInfo.DacType == ATT20C490_DAC) \ + || (pTseng->DacInfo.DacType == ATT20C491_DAC) \ + || (pTseng->DacInfo.DacType == ATT20C492_DAC) \ + || (pTseng->DacInfo.DacType == ATT20C493_DAC) \ + || (pTseng->DacInfo.DacType == MUSIC4910_DAC) ) + +#define DAC_is_GenDAC ( (pTseng->DacInfo.DacType == ICS5341_DAC) \ + || (pTseng->DacInfo.DacType == ICS5301_DAC) ) + +#define DAC_is_STG170x ( (pTseng->DacInfo.DacType == STG1700_DAC) \ + || (pTseng->DacInfo.DacType == STG1702_DAC) \ + || (pTseng->DacInfo.DacType == STG1703_DAC) ) + +#define DAC_IS_CHRONTEL (pTseng->DacInfo.DacType == CH8398_DAC) + +#define Gendac_programmable_clock \ + ( pScrn->progClock && \ + ( (pTseng->ClockChip == CLOCKCHIP_ICS5341) \ + || (pTseng->ClockChip == CLOCKCHIP_ICS5301) \ + ) \ + ) + +#define STG170x_programmable_clock \ + ( pScrn->progClock && (pTseng->ClockChip == CLOCKCHIP_STG1703) ) + +#define ICD2061a_programmable_clock \ + ( pScrn->progClock && (pTseng->ClockChip == CLOCKCHIP_ICD2061A) ) + +#define CH8398_programmable_clock \ + ( pScrn->progClock && (pTseng->ClockChip == CLOCKCHIP_CH8398) ) + +#define ET6000_programmable_clock \ + ( pScrn->progClock && (pTseng->ClockChip == CLOCKCHIP_ET6000) ) + + +/* + * tseng_driver.c for DGA + */ +Bool TsengModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode); +void TsengAdjustFrame(int scrnIndex, int x, int y, int flags); + +/* + * tseng_dga.c + */ +Bool TsengDGAInit(ScreenPtr pScreen); + +/* + * From tseng_bank.c + */ + +int ET4000SetRead(ScreenPtr pScrn, unsigned int iBank); +int ET4000SetWrite(ScreenPtr pScrn, unsigned int iBank); +int ET4000SetReadWrite(ScreenPtr pScrn, unsigned int iBank); +int ET4000W32SetRead(ScreenPtr pScrn, unsigned int iBank); +int ET4000W32SetWrite(ScreenPtr pScrn, unsigned int iBank); +int ET4000W32SetReadWrite(ScreenPtr pScrn, unsigned int iBank); + +/* + * From tseng_clocks.c + */ + +Bool Tseng_check_clockchip(ScrnInfoPtr pScrn); +void tseng_clock_setup(ScrnInfoPtr pScrn); +void TsengcommonCalcClock(long freq, + int min_m, int min_n1, int max_n1, int min_n2, int max_n2, + long freq_min, long freq_max, + unsigned char *mdiv, unsigned char *ndiv); + +/* + * From tseng_ramdac.c + */ + +void tseng_dactopel(void); +unsigned char tseng_dactocomm(void); +unsigned char tseng_getdaccomm(void); +void tseng_setdaccomm(unsigned char comm); + +Bool Check_Tseng_Ramdac(ScrnInfoPtr pScrn); +void tseng_set_ramdac_bpp(ScrnInfoPtr pScrn, DisplayModePtr mode); + +/* + * From tseng_cursor.c + */ + +Bool TsengHWCursorInit(ScreenPtr pScreen); + +/* + * From tseng_dpms.c + */ + +void TsengHVSyncDPMSSet(ScrnInfoPtr pScrn, int PowerManagementMode, int flags); +void TsengCrtcDPMSSet(ScrnInfoPtr pScrn, int PowerManagementMode, int flags); + +/* + * For debugging + */ + +#undef TSENG_DEBUG + +#ifdef TSENG_DEBUG +#define PDEBUG(arg) do { ErrorF(arg); } while (0) +#else +#define PDEBUG(arg) do {} while (0) +#endif + +#endif diff --git a/src/tseng_accel.c b/src/tseng_accel.c new file mode 100644 index 0000000..ace484c --- /dev/null +++ b/src/tseng_accel.c @@ -0,0 +1,936 @@ +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/tseng_accel.c,v 1.33 2001/02/15 17:54:55 eich Exp $ */ + +/* + * ET4/6K acceleration interface. + * + * Uses Harm Hanemaayer's generic acceleration interface (XAA). + * + * Author: Koen Gadeyne + * + * Much of the acceleration code is based on the XF86_W32 server code from + * Glenn Lai. + * + */ + +/* + * if NO_OPTIMIZE is set, some optimizations are disabled. + * + * What it basically tries to do is minimize the amounts of writes to + * accelerator registers, since these are the ones that slow down small + * operations a lot. + */ + +#define NO_OPTIMIZE + +/* + * if ET6K_TRANSPARENCY is set, ScreentoScreenCopy operations (and pattern + * fills) will support transparency. But then the planemask support has to + * be dropped. The default here is to support planemasks, because all Tseng + * chips can do this. Only the ET6000 supports a transparency compare. The + * code could be easily changed to support transparency on the ET6000 and + * planemasks on the others, but that's only useful when transparency is + * more important than planemasks. + */ + +#undef ET6K_TRANSPARENCY + +#include "tseng.h" +#include "tseng_acl.h" +#include "tseng_inline.h" + +#include "miline.h" + +void TsengSync(ScrnInfoPtr pScrn); + +void TsengSetupForSolidFill(ScrnInfoPtr pScrn, + int color, int rop, unsigned int planemask); +void TsengW32iSubsequentSolidFillRect(ScrnInfoPtr pScrn, + int x, int y, int w, int h); +void TsengW32pSubsequentSolidFillRect(ScrnInfoPtr pScrn, + int x, int y, int w, int h); +void Tseng6KSubsequentSolidFillRect(ScrnInfoPtr pScrn, + int x, int y, int w, int h); + +/* void TsengSubsequentFillTrapezoidSolid(); */ + +void TsengSetupForScreenToScreenCopy(ScrnInfoPtr pScrn, + int xdir, int ydir, int rop, + unsigned int planemask, int trans_color); +void TsengSubsequentScreenToScreenCopy(ScrnInfoPtr pScrn, + int x1, int y1, int x2, int y2, int w, int h); + +void TsengSetupForColor8x8PatternFill(ScrnInfoPtr pScrn, + int patx, int paty, int rop, unsigned int planemask, int trans_color); + +void TsengSubsequentColor8x8PatternFillRect(ScrnInfoPtr pScrn, + int patx, int paty, int x, int y, int w, int h); + +void TsengSetupForScanlineImageWrite(ScrnInfoPtr pScrn, + int rop, unsigned int planemask, int trans_color, int bpp, int depth); + +void TsengSubsequentScanlineImageWriteRect(ScrnInfoPtr pScrn, + int x, int y, int w, int h, int skipleft); + +void TsengSubsequentImageWriteScanline(ScrnInfoPtr pScrn, + int bufno); + +#if 0 +void TsengSetupForSolidLine(ScrnInfoPtr pScrn, + int color, int rop, unsigned int planemask); +#endif + +void TsengSubsequentSolidBresenhamLine(ScrnInfoPtr pScrn, + int x, int y, int major, int minor, int err, int len, int octant); + + +/* + * The following function sets up the supported acceleration. Call it from + * the FbInit() function in the SVGA driver. Do NOT initialize any hardware + * in here. That belongs in tseng_init_acl(). + */ +Bool +TsengXAAInit(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + TsengPtr pTseng = TsengPTR(pScrn); + XAAInfoRecPtr pXAAinfo; + BoxRec AvailFBArea; + + PDEBUG(" TsengXAAInit\n"); + pTseng->AccelInfoRec = pXAAinfo = XAACreateInfoRec(); + if (!pXAAinfo) + return FALSE; + + /* + * Set up the main acceleration flags. + */ + pXAAinfo->Flags = PIXMAP_CACHE; + +#ifdef TODO + if (Tseng_bus != T_BUS_PCI) + pXAAinfo->Flags |= COP_FRAMEBUFFER_CONCURRENCY; +#endif + + /* + * The following line installs a "Sync" function, that waits for + * all coprocessor operations to complete. + */ + pXAAinfo->Sync = TsengSync; + + /* W32 and W32i must wait for ACL before changing registers */ + pTseng->need_wait_acl = (Is_W32_W32i || Is_W32p); + + pTseng->line_width = pScrn->displayWidth * pTseng->Bytesperpixel; + +#if 1 + /* + * SolidFillRect. + * + * The W32 and W32i chips don't have a register to set the amount of + * bytes per pixel, and hence they don't skip 1 byte in each 4-byte word + * at 24bpp. Therefor, the FG or BG colors would have to be concatenated + * in video memory (R-G-B-R-G-B-... instead of R-G-B-X-R-G-B-X-..., with + * X = dont' care), plus a wrap value that is a multiple of 3 would have + * to be set. There is no such wrap combination available. + */ +#ifdef OBSOLETE + pXAAinfo->SolidFillFlags |= NO_PLANEMASK; +#endif + + if (!(Is_W32_W32i && (pScrn->bitsPerPixel == 24))) { + pXAAinfo->SetupForSolidFill = TsengSetupForSolidFill; + if (Is_ET6K) { + pXAAinfo->SubsequentSolidFillRect = Tseng6KSubsequentSolidFillRect; + } else if (Is_W32p) + pXAAinfo->SubsequentSolidFillRect = TsengW32pSubsequentSolidFillRect; + else /* W32, W32i */ + pXAAinfo->SubsequentSolidFillRect = TsengW32iSubsequentSolidFillRect; + } +#ifdef TSENG_TRAPEZOIDS + if (Is_ET6K) { + /* disabled for now: not fully compliant yet */ + pXAAinfo->SubsequentFillTrapezoidSolid = TsengSubsequentFillTrapezoidSolid; + } +#endif +#endif + +#if 1 + /* + * SceenToScreenCopy (BitBLT). + * + * Restrictions: On ET6000, we support EITHER a planemask OR + * TRANSPARENCY, but not both (they use the same Pattern map). + * All other chips can't do TRANSPARENCY at all. + */ +#ifdef ET6K_TRANSPARENCY + pXAAinfo->CopyAreaFlags = NO_PLANEMASK; + if (!Is_ET6K) { + pXAAinfo->CopyAreaFlags |= NO_TRANSPARENCY; + } +#else + pXAAinfo->CopyAreaFlags = NO_TRANSPARENCY; +#endif + + pXAAinfo->SetupForScreenToScreenCopy = + TsengSetupForScreenToScreenCopy; + pXAAinfo->SubsequentScreenToScreenCopy = + TsengSubsequentScreenToScreenCopy; +#endif + +#if 0 + /* + * ImageWrite. + * + * SInce this uses off-screen scanline buffers, it is only of use when + * complex ROPs are used. But since the current XAA pixmap cache code + * only works when an ImageWrite is provided, the NO_GXCOPY flag is + * temporarily disabled. + */ + + if (pTseng->AccelImageWriteBufferOffsets[0]) { + pXAAinfo->ScanlineImageWriteFlags = + pXAAinfo->CopyAreaFlags | LEFT_EDGE_CLIPPING /* | NO_GXCOPY */ ; + pXAAinfo->NumScanlineImageWriteBuffers = 2; + pXAAinfo->SetupForScanlineImageWrite = + TsengSetupForScanlineImageWrite; + pXAAinfo->SubsequentScanlineImageWriteRect = + TsengSubsequentScanlineImageWriteRect; + pXAAinfo->SubsequentImageWriteScanline = + TsengSubsequentImageWriteScanline; + + /* calculate memory addresses from video memory offsets */ + for (i = 0; i < pXAAinfo->NumScanlineImageWriteBuffers; i++) { + pTseng->XAAScanlineImageWriteBuffers[i] = + pTseng->FbBase + pTseng->AccelImageWriteBufferOffsets[i]; + } + + /* + * for banked memory, translate those addresses to fall in the + * correct aperture. Imagewrite uses aperture #1, which sits at + * pTseng->FbBase + 0x1A000. + */ + if (!pTseng->UseLinMem) { + for (i = 0; i < pXAAinfo->NumScanlineImageWriteBuffers; i++) { + pTseng->XAAScanlineImageWriteBuffers[i] = + pTseng->XAAScanlineImageWriteBuffers[i] + - pTseng->AccelImageWriteBufferOffsets[0] + + 0x1A000; + } + } + pXAAinfo->ScanlineImageWriteBuffers = pTseng->XAAScanlineImageWriteBuffers; + } +#endif + /* + * 8x8 pattern tiling not possible on W32/i/p chips in 24bpp mode. + * Currently, 24bpp pattern tiling doesn't work at all on those. + * + * FIXME: On W32 cards, pattern tiling doesn't work as expected. + */ + pXAAinfo->Color8x8PatternFillFlags = HARDWARE_PATTERN_PROGRAMMED_ORIGIN; + + pXAAinfo->CachePixelGranularity = 8 * 8; + +#ifdef ET6K_TRANSPARENCY + pXAAinfo->PatternFlags |= HARDWARE_PATTERN_NO_PLANEMASK; + if (Is_ET6K) { + pXAAinfo->PatternFlags |= HARDWARE_PATTERN_TRANSPARENCY; + } +#endif + +#if 0 + /* FIXME! This needs to be fixed for W32 and W32i (it "should work") */ + if ((pScrn->bitsPerPixel != 24) && (Is_W32p || Is_ET6K)) { + pXAAinfo->SetupForColor8x8PatternFill = + TsengSetupForColor8x8PatternFill; + pXAAinfo->SubsequentColor8x8PatternFillRect = + TsengSubsequentColor8x8PatternFillRect; + } +#endif + +#if 0 /*1*/ + /* + * SolidLine. + * + * We use Bresenham by preference, because it supports hardware clipping + * (using the error term). TwoPointLines() is implemented, but not used, + * because clipped lines are not accelerated (hardware clipping support + * is lacking)... + */ + + if (Is_W32p || Is_ET6K) { + /* + * Fill in the hardware linedraw ACL_XY_DIRECTION table + * + * W32BresTable[] converts XAA interface Bresenham octants to direct + * ACL direction register contents. This includes the correct bias + * setting etc. + * + * According to miline.h (but with base 0 instead of base 1 as in + * miline.h), the octants are numbered as follows: + * + * \ | / + * \ 2 | 1 / + * \ | / + * 3 \ | / 0 + * \|/ + * ----------- + * /|\ + * 4 / | \ 7 + * / | \ + * / 5 | 6 \ + * / | \ + * + * In ACL_XY_DIRECTION, bits 2:0 are defined as follows: + * 0: '1' if XDECREASING + * 1: '1' if YDECREASING + * 2: '1' if XMAJOR (== not YMAJOR) + * + * Bit 4 defines the bias. It should be set to '1' for all octants + * NOT passed to miSetZeroLineBias(). i.e. the inverse of the X bias. + * + * (For MS compatible bias, the data book says to set to the same as + * YDIR, i.e. bit 1 of the same register, = '1' if YDECREASING. MS + * bias is towards octants 0..3 (i.e. Y decreasing), hence this + * definition of bit 4) + * + */ + pTseng->BresenhamTable = xnfalloc(8); + if (pTseng->BresenhamTable == NULL) { + xf86Msg(X_ERROR, "Could not malloc Bresenham Table.\n"); + return FALSE; + } + for (i=0; i<8; i++) { + unsigned char zerolinebias = miGetZeroLineBias(pScreen); + pTseng->BresenhamTable[i] = 0xA0; /* command=linedraw, use error term */ + if (i & XDECREASING) pTseng->BresenhamTable[i] |= 0x01; + if (i & YDECREASING) pTseng->BresenhamTable[i] |= 0x02; + if (!(i & YMAJOR)) pTseng->BresenhamTable[i] |= 0x04; + if ((1 << i) & zerolinebias) pTseng->BresenhamTable[i] |= 0x10; + /* ErrorF("BresenhamTable[%d]=0x%x\n", i, pTseng->BresenhamTable[i]); */ + } + + pXAAinfo->SolidLineFlags = 0; + pXAAinfo->SetupForSolidLine = TsengSetupForSolidFill; + pXAAinfo->SubsequentSolidBresenhamLine = + TsengSubsequentSolidBresenhamLine; + /* + * ErrorTermBits is used to limit minor, major and error term, so it + * must be min(errorterm_size, delta_major_size, delta_minor_size) + * But the calculation for major and minor is done on the DOUBLED + * values (as per the Bresenham algorithm), so they can also have 13 + * bits (inside XAA). They are divided by 2 in this driver, so they + * are then again limited to 12 bits. + */ + pXAAinfo->SolidBresenhamLineErrorTermBits = 13; + } +#endif + +#if 1 + /* set up color expansion acceleration */ + if (!TsengXAAInit_Colexp(pScrn)) + return FALSE; +#endif + + + /* + * For Tseng, we set up some often-used values + */ + + switch (pTseng->Bytesperpixel) { /* for MULBPP optimization */ + case 1: + pTseng->powerPerPixel = 0; + pTseng->planemask_mask = 0x000000FF; + pTseng->neg_x_pixel_offset = 0; + break; + case 2: + pTseng->powerPerPixel = 1; + pTseng->planemask_mask = 0x0000FFFF; + pTseng->neg_x_pixel_offset = 1; + break; + case 3: + pTseng->powerPerPixel = 1; + pTseng->planemask_mask = 0x00FFFFFF; + pTseng->neg_x_pixel_offset = 2; /* is this correct ??? */ + break; + case 4: + pTseng->powerPerPixel = 2; + pTseng->planemask_mask = 0xFFFFFFFF; + pTseng->neg_x_pixel_offset = 3; + break; + } + + /* + * Init ping-pong registers. + * This might be obsoleted by the BACKGROUND_OPERATIONS flag. + */ + pTseng->tsengFg = 0; + pTseng->tsengBg = 16; + pTseng->tsengPat = 32; + + /* for register write optimisation */ + pTseng->tseng_old_dir = -1; + pTseng->old_x = 0; + pTseng->old_y = 0; + + /* + * Finally, we set up the video memory space available to the pixmap + * cache. In this case, all memory from the end of the virtual screen to + * the end of video memory minus 1K (which we already reserved), can be + * used. + */ + + AvailFBArea.x1 = 0; + AvailFBArea.y1 = 0; + AvailFBArea.x2 = pScrn->displayWidth; + AvailFBArea.y2 = (pScrn->videoRam * 1024) / + (pScrn->displayWidth * pTseng->Bytesperpixel); + + xf86InitFBManager(pScreen, &AvailFBArea); + + return (XAAInit(pScreen, pXAAinfo)); + +} + +/* + * This is the implementation of the Sync() function. + * + * To avoid pipeline/cache/buffer flushing in the PCI subsystem and the VGA + * controller, we might replace this read-intensive code with a dummy + * accelerator operation that causes a hardware-blocking (wait-states) until + * the running operation is done. + */ +void +TsengSync(ScrnInfoPtr pScrn) +{ + TsengPtr pTseng = TsengPTR(pScrn); + + WAIT_ACL; +} + +/* + * This is the implementation of the SetupForSolidFill function + * that sets up the coprocessor for a subsequent batch for solid + * rectangle fills. + */ +void +TsengSetupForSolidFill(ScrnInfoPtr pScrn, + int color, int rop, unsigned int planemask) +{ + TsengPtr pTseng = TsengPTR(pScrn); + + /* + * all registers are queued in the Tseng chips, except of course for the + * stuff we want to store in off-screen memory. So we have to use a + * ping-pong method for those if we want to avoid having to wait for the + * accelerator when we want to write to these. + */ + +/* ErrorF("S"); */ + + PINGPONG(pTseng); + + wait_acl_queue(pTseng); + + /* + * planemask emulation uses a modified "standard" FG ROP (see ET6000 + * data book p 66 or W32p databook p 37: "Bit masking"). We only enable + * the planemask emulation when the planemask is not a no-op, because + * blitting speed would suffer. + */ + + if ((planemask & pTseng->planemask_mask) != pTseng->planemask_mask) { + SET_FG_ROP_PLANEMASK(rop); + SET_BG_COLOR(pTseng, planemask); + } else { + SET_FG_ROP(rop); + } + SET_FG_COLOR(pTseng, color); + + SET_FUNCTION_BLT; +} + +/* + * This is the implementation of the SubsequentForSolidFillRect function + * that sends commands to the coprocessor to fill a solid rectangle of + * the specified location and size, with the parameters from the SetUp + * call. + * + * Splitting it up between ET4000 and ET6000 avoids lots of chipset type + * comparisons. + */ +void +TsengW32pSubsequentSolidFillRect(ScrnInfoPtr pScrn, + int x, int y, int w, int h) +{ + TsengPtr pTseng = TsengPTR(pScrn); + int destaddr = FBADDR(pTseng, x, y); + + wait_acl_queue(pTseng); + + /* + * Restoring the ACL_SOURCE_ADDRESS here is needed as long as Bresenham + * lines are enabled for >8bpp. Or until XAA allows us to render + * horizontal lines using the same Bresenham code instead of re-routing + * them to FillRectSolid. For XDECREASING lines, the SubsequentBresenham + * code adjusts the ACL_SOURCE_ADDRESS to make sure XDECREASING lines + * are drawn with the correct colors. But if a batch of subsequent + * operations also holds a few horizontal lines, they will be routed to + * here without calling the SetupFor... code again, and the + * ACL_SOURCE_ADDRESS will be wrong. + */ + ACL_SOURCE_ADDRESS(pTseng->AccelColorBufferOffset + pTseng->tsengFg); + + SET_XYDIR(0); /* FIXME: not needed with separate setupforsolidline */ + + SET_XY_4(pTseng, w, h); + START_ACL(pTseng, destaddr); +} + +void +TsengW32iSubsequentSolidFillRect(ScrnInfoPtr pScrn, + int x, int y, int w, int h) +{ + TsengPtr pTseng = TsengPTR(pScrn); + int destaddr = FBADDR(pTseng, x, y); + + wait_acl_queue(pTseng); + + SET_XYDIR(0); + + SET_XY_6(pTseng, w, h); + START_ACL(pTseng, destaddr); +} + +void +Tseng6KSubsequentSolidFillRect(ScrnInfoPtr pScrn, + int x, int y, int w, int h) +{ + TsengPtr pTseng = TsengPTR(pScrn); + int destaddr = FBADDR(pTseng, x, y); + + wait_acl_queue(pTseng); + + /* see comment in TsengW32pSubsequentFillRectSolid */ + ACL_SOURCE_ADDRESS(pTseng->AccelColorBufferOffset + pTseng->tsengFg); + + /* if XYDIR is not reset here, drawing a hardware line in between + * blitting, with the same ROP, color, etc will not cause a call to + * SetupFor... (because linedrawing uses SetupForSolidFill() as its + * Setup() function), and thus the direction register will have been + * changed by the last LineDraw operation. + */ + SET_XYDIR(0); + + SET_XY_6(pTseng, w, h); + START_ACL_6(destaddr); +} + +/* + * This is the implementation of the SetupForScreenToScreenCopy function + * that sets up the coprocessor for a subsequent batch of + * screen-to-screen copies. + */ + +static __inline__ void +Tseng_setup_screencopy(TsengPtr pTseng, + int rop, unsigned int planemask, + int trans_color, int blit_dir) +{ + wait_acl_queue(pTseng); + +#ifdef ET6K_TRANSPARENCY + if (Is_ET6K && (trans_color != -1)) { + SET_BG_COLOR(trans_color); + SET_FUNCTION_BLT_TR; + } else + SET_FUNCTION_BLT; + + SET_FG_ROP(rop); +#else + if ((planemask & pTseng->planemask_mask) != pTseng->planemask_mask) { + SET_FG_ROP_PLANEMASK(rop); + SET_BG_COLOR(pTseng, planemask); + } else { + SET_FG_ROP(rop); + } + SET_FUNCTION_BLT; +#endif + SET_XYDIR(blit_dir); +} + +void +TsengSetupForScreenToScreenCopy(ScrnInfoPtr pScrn, + int xdir, int ydir, int rop, + unsigned int planemask, int trans_color) +{ + /* + * xdir can be either 1 (left-to-right) or -1 (right-to-left). + * ydir can be either 1 (top-to-bottom) or -1 (bottom-to-top). + */ + + TsengPtr pTseng = TsengPTR(pScrn); + int blit_dir = 0; + +/* ErrorF("C%d ", trans_color); */ + + pTseng->acl_blitxdir = xdir; + pTseng->acl_blitydir = ydir; + + if (xdir == -1) + blit_dir |= 0x1; + if (ydir == -1) + blit_dir |= 0x2; + + Tseng_setup_screencopy(pTseng, rop, planemask, trans_color, blit_dir); + + ACL_SOURCE_WRAP(0x77); /* no wrap */ + ACL_SOURCE_Y_OFFSET(pTseng->line_width - 1); +} + +/* + * This is the implementation of the SubsequentForScreenToScreenCopy + * that sends commands to the coprocessor to perform a screen-to-screen + * copy of the specified areas, with the parameters from the SetUp call. + * In this sample implementation, the direction must be taken into + * account when calculating the addresses (with coordinates, it might be + * a little easier). + * + * Splitting up the SubsequentScreenToScreenCopy between ET4000 and ET6000 + * doesn't seem to improve speed for small blits (as it did with + * SolidFillRect). + */ + +void +TsengSubsequentScreenToScreenCopy(ScrnInfoPtr pScrn, + int x1, int y1, int x2, int y2, + int w, int h) +{ + TsengPtr pTseng = TsengPTR(pScrn); + int srcaddr, destaddr; + + /* + * Optimizing note: the pre-calc code below (i.e. until the first + * register write) doesn't significantly affect performance. Removing it + * all boosts small blits from 24.22 to 25.47 MB/sec. Don't waste time + * on that. One less PCI bus write would boost us to 30.00 MB/sec, up + * from 24.22. Waste time on _that_... + */ + + /* tseng chips want x-sizes in bytes, not pixels */ + x1 = MULBPP(pTseng, x1); + x2 = MULBPP(pTseng, x2); + + /* + * If the direction is "decreasing", the chip wants the addresses + * to be at the other end, so we must be aware of that in our + * calculations. + */ + if (pTseng->acl_blitydir == -1) { + srcaddr = (y1 + h - 1) * pTseng->line_width; + destaddr = (y2 + h - 1) * pTseng->line_width; + } else { + srcaddr = y1 * pTseng->line_width; + destaddr = y2 * pTseng->line_width; + } + if (pTseng->acl_blitxdir == -1) { + /* Accelerator start address must point to first byte to be processed. + * Depending on the direction, this is the first or the last byte + * in the multi-byte pixel. + */ + int eol = MULBPP(pTseng, w); + + srcaddr += x1 + eol - 1; + destaddr += x2 + eol - 1; + } else { + srcaddr += x1; + destaddr += x2; + } + + wait_acl_queue(pTseng); + + SET_XY(pTseng, w, h); + ACL_SOURCE_ADDRESS(srcaddr); + START_ACL(pTseng, destaddr); +} + +static int pat_src_addr; + +void +TsengSetupForColor8x8PatternFill(ScrnInfoPtr pScrn, + int patx, int paty, int rop, unsigned int planemask, int trans_color) +{ + TsengPtr pTseng = TsengPTR(pScrn); + + pat_src_addr = FBADDR(pTseng, patx, paty); + + ErrorF("P"); + + Tseng_setup_screencopy(pTseng, rop, planemask, trans_color, 0); + + switch (pTseng->Bytesperpixel) { + case 1: + ACL_SOURCE_WRAP(0x33); /* 8x8 wrap */ + ACL_SOURCE_Y_OFFSET(8 - 1); + break; + case 2: + ACL_SOURCE_WRAP(0x34); /* 16x8 wrap */ + ACL_SOURCE_Y_OFFSET(16 - 1); + break; + case 3: + ACL_SOURCE_WRAP(0x3D); /* 24x8 wrap --- only for ET6000 !!! */ + ACL_SOURCE_Y_OFFSET(32 - 1); /* this is no error -- see databook */ + break; + case 4: + ACL_SOURCE_WRAP(0x35); /* 32x8 wrap */ + ACL_SOURCE_Y_OFFSET(32 - 1); + } +} + +void +TsengSubsequentColor8x8PatternFillRect(ScrnInfoPtr pScrn, + int patx, int paty, int x, int y, int w, int h) +{ + TsengPtr pTseng = TsengPTR(pScrn); + int destaddr = FBADDR(pTseng, x, y); + int srcaddr = pat_src_addr + MULBPP(pTseng, paty * 8 + patx); + + wait_acl_queue(pTseng); + + ACL_SOURCE_ADDRESS(srcaddr); + + SET_XY(pTseng, w, h); + START_ACL(pTseng, destaddr); +} + +/* + * ImageWrite is nothing more than a per-scanline screencopy. + */ + +void +TsengSetupForScanlineImageWrite(ScrnInfoPtr pScrn, + int rop, unsigned int planemask, int trans_color, int bpp, int depth) +{ + TsengPtr pTseng = TsengPTR(pScrn); + +/* ErrorF("IW"); */ + + Tseng_setup_screencopy(pTseng, rop, planemask, trans_color, 0); + + ACL_SOURCE_WRAP(0x77); /* no wrap */ + ACL_SOURCE_Y_OFFSET(pTseng->line_width - 1); +} + +void +TsengSubsequentScanlineImageWriteRect(ScrnInfoPtr pScrn, + int x, int y, int w, int h, int skipleft) +{ + TsengPtr pTseng = TsengPTR(pScrn); + +/* ErrorF("r%d",h); */ + + pTseng->acl_iw_dest = y * pTseng->line_width + MULBPP(pTseng, x); + pTseng->acl_skipleft = MULBPP(pTseng, skipleft); + + wait_acl_queue(pTseng); + SET_XY(pTseng, w, 1); +} + +void +TsengSubsequentImageWriteScanline(ScrnInfoPtr pScrn, + int bufno) +{ + TsengPtr pTseng = TsengPTR(pScrn); + +/* ErrorF("%d", bufno); */ + + wait_acl_queue(pTseng); + + ACL_SOURCE_ADDRESS(pTseng->AccelImageWriteBufferOffsets[bufno] + + pTseng->acl_skipleft); + START_ACL(pTseng, pTseng->acl_iw_dest); + pTseng->acl_iw_dest += pTseng->line_width; +} + +/* + * W32p/ET6000 hardware linedraw code. + * + * TsengSetupForSolidFill() is used as a setup function. + * + * Three major problems that needed to be solved here: + * + * 1. The "bias" value must be translated into the "line draw algorithm" + * parameter in the Tseng accelerators. This parameter, although not + * documented as such, needs to be set to the _inverse_ of the + * appropriate bias bit (i.e. for the appropriate octant). + * + * 2. In >8bpp modes, the accelerator will render BYTES in the same order as + * it is drawing the line. This means it will render the colors in the + * same order as well, reversing the byte-order in pixels that are drawn + * right-to-left. This causes wrong colors to be rendered. + * + * 3. The Tseng data book says that the ACL Y count register needs to be + * programmed with "dy-1". A similar thing is said about ACL X count. But + * this assumes (x2,y2) is NOT drawn (although that is not mentionned in + * the data book). X assumes the endpoint _is_ drawn. If "dy-1" is used, + * this sometimes results in a negative value (if dx==dy==0), + * causing a complete accelerator hang. + */ + +void +TsengSubsequentSolidBresenhamLine(ScrnInfoPtr pScrn, + int x, int y, int major, int minor, int err, int len, int octant) +{ + TsengPtr pTseng = TsengPTR(pScrn); + int destaddr = FBADDR(pTseng, x, y); + int xydir = pTseng->BresenhamTable[octant]; + + /* Tseng wants the real dx/dy in major/minor. Bresenham uses 2*dx and 2*dy */ + minor >>= 1; + major >>= 1; + + wait_acl_queue(pTseng); + + if (!(octant & YMAJOR)) { + SET_X_YRAW(pTseng, len, 0xFFF); + } else { + SET_XY_RAW(pTseng,0xFFF, len - 1); + } + + SET_DELTA(minor, major); + ACL_ERROR_TERM(-err); /* error term from XAA is NEGATIVE */ + + /* make sure colors are rendered correctly if >8bpp */ + if (octant & XDECREASING) { + destaddr += pTseng->Bytesperpixel - 1; + ACL_SOURCE_ADDRESS(pTseng->AccelColorBufferOffset + + pTseng->tsengFg + pTseng->neg_x_pixel_offset); + } else + ACL_SOURCE_ADDRESS(pTseng->AccelColorBufferOffset + pTseng->tsengFg); + + SET_XYDIR(xydir); + + START_ACL(pTseng, destaddr); +} + + +#ifdef TODO +/* + * Trapezoid filling code. + * + * TsengSetupForSolidFill() is used as a setup function + */ + +#undef DEBUG_TRAP + +#ifdef TSENG_TRAPEZOIDS +void +TsengSubsequentFillTrapezoidSolid(ytop, height, left, dxL, dyL, eL, right, dxR, dyR, eR) + int ytop; + int height; + int left; + int dxL, dyL; + int eL; + int right; + int dxR, dyR; + int eR; +{ + unsigned int tseng_bias_compensate = 0xd8; + int destaddr, algrthm; + int xcount = right - left + 1; /* both edges included */ + int dir_reg = 0x60; /* trapezoid drawing; use error term for primary edge */ + int sec_dir_reg = 0x20; /* use error term for secondary edge */ + int octant = 0; + + /* ErrorF("#"); */ + + int destaddr, algrthm; + int xcount = right - left + 1; + +#ifdef USE_ERROR_TERM + int dir_reg = 0x60; + int sec_dir_reg = 0x20; + +#else + int dir_reg = 0x40; + int sec_dir_reg = 0x00; + +#endif + int octant = 0; + int bias = 0x00; /* FIXME !!! */ + +/* ErrorF("#"); */ + +#ifdef DEBUG_TRAP + ErrorF("ytop=%d, height=%d, left=%d, dxL=%d, dyL=%d, eL=%d, right=%d, dxR=%d, dyR=%d, eR=%d ", + ytop, height, left, dxL, dyL, eL, right, dxR, dyR, eR); +#endif + + if ((dyL < 0) || (dyR < 0)) + ErrorF("Tseng Trapezoids: Wrong assumption: dyL/R < 0\n"); + + destaddr = FBADDR(pTseng, left, ytop); + + /* left edge */ + if (dxL < 0) { + dir_reg |= 1; + octant |= XDECREASING; + dxL = -dxL; + } + /* Y direction is always positive (top-to-bottom drawing) */ + + wait_acl_queue(pTseng); + + /* left edge */ + /* compute axial direction and load registers */ + if (dxL >= dyL) { /* X is major axis */ + dir_reg |= 4; + SET_DELTA(dyL, dxL); + if (dir_reg & 1) { /* edge coherency: draw left edge */ + destaddr += pTseng->Bytesperpixel; + sec_dir_reg |= 0x80; + xcount--; + } + } else { /* Y is major axis */ + SetYMajorOctant(octant); + SET_DELTA(dxL, dyL); + } + ACL_ERROR_TERM(eL); + + /* select "linedraw algorithm" (=bias) and load direction register */ + /* ErrorF(" o=%d ", octant); */ + algrthm = ((tseng_bias_compensate >> octant) & 1) ^ 1; + dir_reg |= algrthm << 4; + SET_XYDIR(dir_reg); + + /* right edge */ + if (dxR < 0) { + sec_dir_reg |= 1; + dxR = -dxR; + } + /* compute axial direction and load registers */ + if (dxR >= dyR) { /* X is major axis */ + sec_dir_reg |= 4; + SET_SECONDARY_DELTA(dyR, dxR); + if (dir_reg & 1) { /* edge coherency: do not draw right edge */ + sec_dir_reg |= 0x40; + xcount++; + } + } else { /* Y is major axis */ + SET_SECONDARY_DELTA(dxR, dyR); + } + ACL_SECONDARY_ERROR_TERM(eR); + + /* ErrorF("%02x", sec_dir_reg); */ + SET_SECONDARY_XYDIR(sec_dir_reg); + + SET_XY_6(pTseng, xcount, height); + +#ifdef DEBUG_TRAP + ErrorF("-> %d,%d\n", xcount, height); +#endif + + START_ACL_6(destaddr); +} +#endif + +#endif diff --git a/src/tseng_acl.c b/src/tseng_acl.c new file mode 100644 index 0000000..9a62e4e --- /dev/null +++ b/src/tseng_acl.c @@ -0,0 +1,232 @@ + +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/tseng_acl.c,v 1.25 2000/12/14 16:33:10 eich Exp $ */ + + + + + +#include "tseng.h" +#include "tseng_acl.h" +#include "compiler.h" + +void tseng_terminate_acl(TsengPtr pTseng); + +/* + * conversion from X ROPs to Microsoft ROPs. + */ + +int W32OpTable[] = +{ + 0x00, /* Xclear 0 */ + 0x88, /* Xand src AND dst */ + 0x44, /* XandReverse src AND NOT dst */ + 0xcc, /* Xcopy src */ + 0x22, /* XandInverted NOT src AND dst */ + 0xaa, /* Xnoop dst */ + 0x66, /* Xxor src XOR dst */ + 0xee, /* Xor src OR dst */ + 0x11, /* Xnor NOT src AND NOT dst */ + 0x99, /* Xequiv NOT src XOR dst */ + 0x55, /* Xinvert NOT dst */ + 0xdd, /* XorReverse src OR NOT dst */ + 0x33, /* XcopyInverted NOT src */ + 0xbb, /* XorInverted NOT src OR dst */ + 0x77, /* Xnand NOT src OR NOT dst */ + 0xff /* Xset 1 */ +}; + +int W32OpTable_planemask[] = +{ + 0x0a, /* Xclear 0 */ + 0x8a, /* Xand src AND dst */ + 0x4a, /* XandReverse src AND NOT dst */ + 0xca, /* Xcopy src */ + 0x2a, /* XandInverted NOT src AND dst */ + 0xaa, /* Xnoop dst */ + 0x6a, /* Xxor src XOR dst */ + 0xea, /* Xor src OR dst */ + 0x1a, /* Xnor NOT src AND NOT dst */ + 0x9a, /* Xequiv NOT src XOR dst */ + 0x5a, /* Xinvert NOT dst */ + 0xda, /* XorReverse src OR NOT dst */ + 0x3a, /* XcopyInverted NOT src */ + 0xba, /* XorInverted NOT src OR dst */ + 0x7a, /* Xnand NOT src OR NOT dst */ + 0xfa /* Xset 1 */ +}; + +int W32PatternOpTable[] = +{ + 0x00, /* Xclear 0 */ + 0xa0, /* Xand pat AND dst */ + 0x50, /* XandReverse pat AND NOT dst */ + 0xf0, /* Xcopy pat */ + 0x0a, /* XandInverted NOT pat AND dst */ + 0xaa, /* Xnoop dst */ + 0x5a, /* Xxor pat XOR dst */ + 0xfa, /* Xor pat OR dst */ + 0x05, /* Xnor NOT pat AND NOT dst */ + 0xa5, /* Xequiv NOT pat XOR dst */ + 0x55, /* Xinvert NOT dst */ + 0xf5, /* XorReverse pat OR NOT dst */ + 0x0f, /* XcopyInverted NOT pat */ + 0xaf, /* XorInverted NOT pat OR dst */ + 0x5f, /* Xnand NOT pat OR NOT dst */ + 0xff /* Xset 1 */ +}; + + + +/**********************************************************************/ + +void +tseng_terminate_acl(TsengPtr pTseng) +{ + /* only terminate when needed */ +/* if (*(volatile unsigned char *)ACL_ACCELERATOR_STATUS & 0x06) */ + { + ACL_SUSPEND_TERMINATE(0x00); + /* suspend any running operation */ + ACL_SUSPEND_TERMINATE(0x01); + WAIT_ACL; + ACL_SUSPEND_TERMINATE(0x00); + /* ... and now terminate it */ + ACL_SUSPEND_TERMINATE(0x10); + WAIT_ACL; + ACL_SUSPEND_TERMINATE(0x00); + } +} + +void +tseng_recover_timeout(TsengPtr pTseng) +{ + if (!Is_ET6K) { + ErrorF("trying to unlock......................................\n"); + MMIO_OUT32(pTseng->tsengCPU2ACLBase,0,0L); /* try unlocking the bus when CPU-to-accel gets stuck */ + } + if (Is_W32p) { /* flush the accelerator pipeline */ + ACL_SUSPEND_TERMINATE(0x00); + ACL_SUSPEND_TERMINATE(0x02); + ACL_SUSPEND_TERMINATE(0x00); + } +} + +void +tseng_init_acl(ScrnInfoPtr pScrn) +{ + TsengPtr pTseng = TsengPTR(pScrn); + + PDEBUG(" tseng_init_acl\n"); + /* + * prepare some shortcuts for faster access to memory mapped registers + */ + + if (pTseng->UseLinMem) { + pTseng->scratchMemBase = pTseng->FbBase + pTseng->AccelColorBufferOffset; + /* + * we won't be using tsengCPU2ACLBase in linear memory mode anyway, since + * using the MMU apertures restricts the amount of useable video memory + * to only 2MB, supposing we ONLY redirect MMU aperture 2 to the CPU. + * (see data book W32p, page 207) + */ + pTseng->tsengCPU2ACLBase = pTseng->FbBase + 0x200000; /* MMU aperture 2 */ + } else { + /* + * MMU 0 is used for the scratchpad (i.e. FG and BG colors). + * + * MMU 1 is used for the Imagewrite buffers. This code assumes those + * buffers are back-to-back, with AccelImageWriteBufferOffsets[0] + * being the first, and don't exceed 8kb (aperture size) in total + * length. + */ + pTseng->scratchMemBase = pTseng->FbBase + 0x18000L; + MMIO_OUT32(pTseng->MMioBase, 0x00<<0, pTseng->AccelColorBufferOffset); + MMIO_OUT32(pTseng->MMioBase, 0x04<<0, pTseng->AccelImageWriteBufferOffsets[0]); + /* + * tsengCPU2ACLBase is used for CPUtoSCreen...() operations on < ET6000 devices + */ + pTseng->tsengCPU2ACLBase = pTseng->FbBase + 0x1C000L; /* MMU aperture 2 */ + /* MMIO_IN32(pTseng->MMioBase, 0x08<<0) = 200000; *//* TEST */ + } +#ifdef DEBUG + ErrorF("MMioBase = 0x%x, scratchMemBase = 0x%x\n", pTseng->MMioBase, pTseng->scratchMemBase); +#endif + + /* + * prepare the accelerator for some real work + */ + + tseng_terminate_acl(pTseng); + + ACL_INTERRUPT_STATUS(0xe); /* clear interrupts */ + ACL_INTERRUPT_MASK(0x04); /* disable interrupts, but enable deadlock exit */ + ACL_INTERRUPT_STATUS(0x0); + ACL_ACCELERATOR_STATUS_SET(0x0); + + if (Is_ET6K) { + ACL_STEPPING_INHIBIT(0x0); /* Undefined at power-on, let all maps (Src, Dst, Mix, Pat) step */ + ACL_6K_CONFIG(0x00); /* maximum performance -- what did you think? */ + ACL_POWER_CONTROL(0x01); /* conserve power when ACL is idle */ + ACL_MIX_CONTROL(0x33); + ACL_TRANSFER_DISABLE(0x00); /* Undefined at power-on, enable all transfers */ + } else { /* W32i/W32p */ + ACL_RELOAD_CONTROL(0x0); + ACL_SYNC_ENABLE(0x1); /* | 0x2 = 0WS ACL read. Yields up to 10% faster operation for small blits */ + ACL_ROUTING_CONTROL(0x00); + } + + if (Is_W32p || Is_ET6K) { + /* Enable the W32p startup bit and set use an eight-bit pixel depth */ + ACL_NQ_X_POSITION(0); + ACL_NQ_Y_POSITION(0); + ACL_PIXEL_DEPTH((pScrn->bitsPerPixel - 8) << 1); + /* writing destination address will start ACL */ + ACL_OPERATION_STATE(0x10); + } else { + /* X, Y positions set to zero's for w32 and w32i */ + ACL_X_POSITION(0); + ACL_Y_POSITION(0); + ACL_OPERATION_STATE(0x0); + /* if we ever use CPU-to-screen pixmap uploading on W32I or W32, + * ACL_VIRTUAL_BUS_SIZE will need to be made dynamic (i.e. moved to + * Setup() functions). + * + * VBS = 1 byte is faster than VBS = 4 bytes, since the ACL can + * start processing as soon as the first byte arrives. + */ + ACL_VIRTUAL_BUS_SIZE(0x00); + } + ACL_DESTINATION_Y_OFFSET(pScrn->displayWidth * pTseng->Bytesperpixel - 1); + ACL_XY_DIRECTION(0); + + MMU_CONTROL(0x74); + + if (Is_W32p && pTseng->UseLinMem) { + /* + * Since the w32p revs C and D don't have any memory mapped when the + * accelerator registers are used it is necessary to use the MMUs to + * provide a semblance of linear memory. Fortunately on these chips + * the MMU appertures are 1 megabyte each. So as long as we are + * willing to only use 3 megs of video memory we can have some + * acceleration. If we ever get the CPU-to-screen-color-expansion + * stuff working then we will NOT need to sacrifice the extra 1MB + * provided by MBP2, because we could do dynamic switching of the APT + * bit in the MMU control register. + * + * On W32p rev c and d MBP2 is hardwired to 0x200000 when linear + * memory mode is enabled. (On rev a it is programmable). + * + * W32p rev a and b have their first 2M mapped in the normal (non-MMU) + * way, and MMU0 and MMU1, each 512 kb wide, can be used to access + * another 1MB of memory. This totals to 3MB of mem. available in + * linear memory when the accelerator is enabled. + */ + if (Is_W32p_ab) { + MMIO_OUT32(pTseng->MMioBase, 0x00<<0, 0x200000L); + MMIO_OUT32(pTseng->MMioBase, 0x04<<0, 0x280000L); + } else { /* rev C & D */ + MMIO_OUT32(pTseng->MMioBase, 0x00<<0, 0x0L); + MMIO_OUT32 (pTseng->MMioBase, 0x04<<0, 0x100000L); + } + } +} diff --git a/src/tseng_acl.h b/src/tseng_acl.h new file mode 100644 index 0000000..b7ca4b3 --- /dev/null +++ b/src/tseng_acl.h @@ -0,0 +1,232 @@ + +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/tseng_acl.h,v 1.20 2000/12/14 16:33:10 eich Exp $ */ + + +#ifndef _TSENG_ACL_H +#define _TSENG_ACL_H + +/* + * if NO_OPTIMIZE is set, some optimizations are disabled. + * + * What it basically tries to do is minimize the amounts of writes to + * accelerator registers, since these are the ones that slow down small + * operations a lot. + */ + +#undef NO_OPTIMIZE + +typedef volatile unsigned char *ByteP; +typedef volatile unsigned short *WordP; +typedef volatile unsigned *LongP; + +void tseng_recover_timeout(TsengPtr pTseng); + +/* + * Shortcuts to Tseng memory-mapped accelerator-control registers + */ + +#if 0 +#endif + +#define MMU_CONTROL(x) MMIO_OUT8(pTseng->MMioBase, 0x13<<0, x) +#define ACL_SUSPEND_TERMINATE(x) MMIO_OUT8(pTseng->MMioBase, 0x30<<0, x) +#define ACL_OPERATION_STATE(x) MMIO_OUT8(pTseng->MMioBase, 0x31<<0, x) + +#define ACL_SYNC_ENABLE(x) MMIO_OUT8(pTseng->MMioBase, 0x32<<0, x) + /* for ET6000, ACL_SYNC_ENABLE becomes ACL_6K_CONFIG */ + +#define ACL_INTERRUPT_STATUS(x) \ + MMIO_OUT8(pTseng->MMioBase, 0x35<<0, x) +#define ACL_INTERRUPT_MASK(x) MMIO_OUT8(pTseng->MMioBase, 0x34<<0, x) +#define ACL_ACCELERATOR_STATUS (0x36 << 0) +#define ACL_ACCELERATOR_STATUS_SET(x) \ + MMIO_OUT8(pTseng->MMioBase, ACL_ACCELERATOR_STATUS, x) +#define ACL_WRITE_INTERFACE_VALID (0x33 << 0) + + /* and this is only for the ET6000 */ +#define ACL_POWER_CONTROL(x) MMIO_OUT8(pTseng->MMioBase, 0x37<<0, x) + + /* non-queued for w32p's and ET6000 */ +#define ACL_NQ_X_POSITION(x) MMIO_OUT16(pTseng->MMioBase, 0x38<<0, x) +#define ACL_NQ_Y_POSITION(x) MMIO_OUT16(pTseng->MMioBase, 0x3A<<0, x) + /* queued for w32 and w32i */ +#define ACL_X_POSITION(x) MMIO_OUT16(pTseng->MMioBase, 0x94<<0, x) +#define ACL_Y_POSITION(x) MMIO_OUT16(pTseng->MMioBase, 0x96<<0, x) + +#define ACL_PATTERN_ADDRESS(x) MMIO_OUT32(pTseng->MMioBase, 0x80<<0, x) +#define ACL_SOURCE_ADDRESS(x) MMIO_OUT32(pTseng->MMioBase, 0x84<<0, x) + +#define ACL_PATTERN_Y_OFFSET(x) MMIO_OUT16(pTseng->MMioBase, 0x88<<0, x) +#define ACL_PATTERN_Y_OFFSET32(x) MMIO_OUT32(pTseng->MMioBase, 0x88<<0, x) +#define ACL_SOURCE_Y_OFFSET(x) MMIO_OUT16(pTseng->MMioBase, 0x8A<<0, x) +#define ACL_DESTINATION_Y_OFFSET(x) MMIO_OUT16(pTseng->MMioBase, 0x8C<<0, x) + + /* W32i */ +#define ACL_VIRTUAL_BUS_SIZE(x) MMIO_OUT8(pTseng->MMioBase, 0x8E<<0, x) + /* w32p */ +#define ACL_PIXEL_DEPTH(x) MMIO_OUT8(pTseng->MMioBase, 0x8E<<0, x) + + /* w32 and w32i */ +#define ACL_XY_DIRECTION(x) MMIO_OUT8(pTseng->MMioBase, 0x8F<<0, x) + +#define ACL_PATTERN_WRAP(x) MMIO_OUT8(pTseng->MMioBase, 0x90<<0, x) +#define ACL_PATTERN_WRAP32(x) MMIO_OUT32(pTseng->MMioBase, 0x90<<0, x) +#define ACL_TRANSFER_DISABLE(x) MMIO_OUT8(pTseng->MMioBase, 0x91<<0, x) /* ET6000 only */ +#define ACL_SOURCE_WRAP(x) MMIO_OUT8(pTseng->MMioBase, 0x92<<0, x) + +#define ACL_X_COUNT(x) MMIO_OUT16(pTseng->MMioBase, 0x98<<0, x) +#define ACL_Y_COUNT(x) MMIO_OUT16(pTseng->MMioBase, 0x9A<<0, x) +/* shortcut. not a real register */ +#define ACL_XY_COUNT(x) MMIO_OUT32(pTseng->MMioBase, 0x98<<0, x) + +#define ACL_ROUTING_CONTROL(x) MMIO_OUT8(pTseng->MMioBase, 0x9C<<0, x) + /* for ET6000, ACL_ROUTING_CONTROL becomes ACL_MIX_CONTROL */ +#define ACL_RELOAD_CONTROL(x) MMIO_OUT8(pTseng->MMioBase, 0x9D<<0, x) + /* for ET6000, ACL_RELOAD_CONTROL becomes ACL_STEPPING_INHIBIT */ + +#define ACL_BACKGROUND_RASTER_OPERATION(x) MMIO_OUT8(pTseng->MMioBase, 0x9E<<0, x) +#define ACL_FOREGROUND_RASTER_OPERATION(x) MMIO_OUT8(pTseng->MMioBase, 0x9F<<0, x) + +#define ACL_DESTINATION_ADDRESS(x) MMIO_OUT32(pTseng->MMioBase, 0xA0<<0, x) + + /* the following is for the w32p's only */ +#define ACL_MIX_ADDRESS(x) MMIO_OUT32(pTseng->MMioBase, 0xA4<<0, x) + +#define ACL_MIX_Y_OFFSET(x) MMIO_OUT16(pTseng->MMioBase, 0xA8<<0, x) +#define ACL_ERROR_TERM(x) MMIO_OUT16(pTseng->MMioBase, 0xAA<<0, x) +#define ACL_DELTA_MINOR(x) MMIO_OUT16(pTseng->MMioBase, 0xAC<<0, x) +#define ACL_DELTA_MINOR32(x) MMIO_OUT32(pTseng->MMioBase, 0xAC<<0, x) +#define ACL_DELTA_MAJOR(x) MMIO_OUT16(pTseng->MMioBase, 0xAE<<0, x) + + /* ET6000 only (trapezoids) */ +#define ACL_SECONDARY_EDGE(x) MMIO_OUT8(pTseng->MMioBase, 0x93<<0, x) +#define ACL_SECONDARY_ERROR_TERM(x) MMIO_OUT16(pTseng->MMioBase, 0xB2<<0, x) +#define ACL_SECONDARY_DELTA_MINOR(x) MMIO_OUT16(pTseng->MMioBase, 0xB4<<0, x) +#define ACL_SECONDARY_DELTA_MINOR32(x) MMIO_OUT32(pTseng->MMioBase, 0xB4<<0, x) +#define ACL_SECONDARY_DELTA_MAJOR(x) MMIO_OUT16(pTseng->MMioBase, 0xB6<<0, x) + +/* for ET6000: */ +#define ACL_6K_CONFIG ACL_SYNC_ENABLE + +/* for ET6000: */ +#define ACL_MIX_CONTROL ACL_ROUTING_CONTROL +#define ACL_STEPPING_INHIBIT ACL_RELOAD_CONTROL + + +/* + * Some data structures for faster accelerator programming. + */ + +extern int W32OpTable[16]; +extern int W32OpTable_planemask[16]; +extern int W32PatternOpTable[16]; + +/* + * Some shortcuts. + */ + +#define MAX_WAIT_CNT 500000 /* how long we wait before we time out */ +#undef WAIT_VERBOSE /* if defined: print out how long we waited */ + +static __inline__ void +tseng_wait(TsengPtr pTseng, int reg, char *name, unsigned char mask) +{ + int cnt = MAX_WAIT_CNT; + + while ((MMIO_IN32(pTseng->MMioBase,reg)) & mask) + if (--cnt < 0) { + ErrorF("WAIT_%s: timeout.\n", name); + tseng_recover_timeout(pTseng); + break; + } +#ifdef WAIT_VERBOSE + ErrorF("%s%d ", name, MAX_WAIT_CNT - cnt); +#endif +} + +#define WAIT_QUEUE tseng_wait(pTseng, ACL_ACCELERATOR_STATUS, "QUEUE", 0x1) + +/* This is only for W32p rev b...d */ +#define WAIT_INTERFACE tseng_wait(pTseng, ACL_WRITE_INTERFACE_VALID, "INTERFACE", 0xf) + +#define WAIT_ACL tseng_wait(pTseng, ACL_ACCELERATOR_STATUS, "ACL", 0x2) + +#define WAIT_XY tseng_wait(pTseng, ACL_ACCELERATOR_STATUS, "XY", 0x4) + +#define SET_FUNCTION_BLT \ + if (Is_ET6K) \ + ACL_MIX_CONTROL(0x33); \ + else \ + ACL_ROUTING_CONTROL(0x00); + +#define SET_FUNCTION_BLT_TR \ + ACL_MIX_CONTROL(0x13); + +#define FBADDR(pTseng, x,y) ( (y) * pTseng->line_width + MULBPP(pTseng, x) ) + +#define SET_FG_ROP(rop) \ + ACL_FOREGROUND_RASTER_OPERATION(W32OpTable[rop]); + +#define SET_FG_ROP_PLANEMASK(rop) \ + ACL_FOREGROUND_RASTER_OPERATION(W32OpTable_planemask[rop]); + +#define SET_BG_ROP(rop) \ + ACL_BACKGROUND_RASTER_OPERATION(W32PatternOpTable[rop]); + +#define SET_BG_ROP_TR(rop, bg_color) \ + if ((bg_color) == -1) /* transparent color expansion */ \ + ACL_BACKGROUND_RASTER_OPERATION(0xaa); \ + else \ + ACL_BACKGROUND_RASTER_OPERATION(W32PatternOpTable[rop]); + +#define SET_DELTA(Min, Maj) \ + ACL_DELTA_MINOR32(((Maj) << 16) + (Min)) + +#define SET_SECONDARY_DELTA(Min, Maj) \ + ACL_SECONDARY_DELTA_MINOR(((Maj) << 16) + (Min)) + +#ifdef NO_OPTIMIZE +#define SET_XYDIR(dir) \ + ACL_XY_DIRECTION(dir); +#else +/* + * only changing ACL_XY_DIRECTION when it needs to be changed avoids + * unnecessary PCI bus writes, which are slow. This shows up very well + * on consecutive small fills. + */ +#define SET_XYDIR(dir) \ + if ((dir) != pTseng->tseng_old_dir) \ + pTseng->tseng_old_dir = (dir); \ + ACL_XY_DIRECTION(pTseng->tseng_old_dir); +#endif + +#define SET_SECONDARY_XYDIR(dir) \ + ACL_SECONDARY_EDGE(dir); + +/* Must do 0x09 (in one operation) for the W32 */ +#define START_ACL(pTseng, dst) \ + ACL_DESTINATION_ADDRESS(dst); \ + if (Is_W32 || Is_W32i) ACL_OPERATION_STATE(0x09); + +/* START_ACL for the ET6000 */ +#define START_ACL_6(dst) \ + ACL_DESTINATION_ADDRESS(dst); + +#define START_ACL_CPU(pTseng, dst) \ + if (Is_W32 || Is_W32i) \ + MMIO_OUT32(pTseng->MMioBase, 0x08<<8,(CARD32)dst); /* writing to MMU2 will trigger accel at this address */ \ + else \ + ACL_DESTINATION_ADDRESS(dst); + +/* ACL_DESTINATION_ADDRESS(dst); should be enough for START_ACL_CPU */ + +/***********************************************************************/ + +void tseng_init_acl(ScrnInfoPtr pScrn); + +Bool TsengXAAInit(ScreenPtr pScreen); + +Bool TsengXAAInit_Colexp(ScrnInfoPtr pScrn); + +#endif + diff --git a/src/tseng_bank.c b/src/tseng_bank.c new file mode 100644 index 0000000..e0d6136 --- /dev/null +++ b/src/tseng_bank.c @@ -0,0 +1,87 @@ + +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/tseng_bank.c,v 1.4 2000/08/08 08:58:06 eich Exp $ */ + + + + + +#include "tseng.h" + +/* + * Tseng really screwed up when they decided to combine the read and write + * bank selectors into one register. Now we need to cache the bank + * registers, because IO reads are too expensive. + */ + + +int +ET4000W32SetRead(ScreenPtr pScreen, unsigned int iBank) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + TsengPtr pTseng = TsengPTR(pScrn); + + pTseng->cache_SegSelL = (pTseng->cache_SegSelL & 0x0f) | (iBank << 4); + pTseng->cache_SegSelH = (pTseng->cache_SegSelH & 0x03) | (iBank & 0x30); + outb(0x3CB, pTseng->cache_SegSelH); + outb(0x3CD, pTseng->cache_SegSelL); + return 0; +} + +int +ET4000W32SetWrite(ScreenPtr pScreen, unsigned int iBank) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + TsengPtr pTseng = TsengPTR(pScrn); + + pTseng->cache_SegSelL = (pTseng->cache_SegSelL & 0xf0) | (iBank & 0x0f); + pTseng->cache_SegSelH = (pTseng->cache_SegSelH & 0x30) | (iBank >> 4); + outb(0x3CB, pTseng->cache_SegSelH); + outb(0x3CD, pTseng->cache_SegSelL); + return 0; +} + +int +ET4000W32SetReadWrite(ScreenPtr pScreen, unsigned int iBank) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + TsengPtr pTseng = TsengPTR(pScrn); + + pTseng->cache_SegSelL = (iBank & 0x0f) | (iBank << 4); + pTseng->cache_SegSelH = (iBank & 0x30) | (iBank >> 4); + outb(0x3CB, pTseng->cache_SegSelH); + outb(0x3CD, pTseng->cache_SegSelL); + return 0; +} + +int +ET4000SetRead(ScreenPtr pScreen, unsigned int iBank) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + TsengPtr pTseng = TsengPTR(pScrn); + + pTseng->cache_SegSelL = (pTseng->cache_SegSelL & 0x0f) | (iBank << 4); + outb(0x3CD, pTseng->cache_SegSelL); + return 0; +} + +int +ET4000SetWrite(ScreenPtr pScreen, unsigned int iBank) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + TsengPtr pTseng = TsengPTR(pScrn); + + pTseng->cache_SegSelL = (pTseng->cache_SegSelL & 0xf0) | (iBank & 0x0f); + outb(0x3CD, pTseng->cache_SegSelL); + return 0; +} + +int +ET4000SetReadWrite(ScreenPtr pScreen, unsigned int iBank) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + TsengPtr pTseng = TsengPTR(pScrn); + + pTseng->cache_SegSelL = (iBank & 0x0f) | (iBank << 4); + outb(0x3CD, pTseng->cache_SegSelL); + return 0; +} diff --git a/src/tseng_clock.c b/src/tseng_clock.c new file mode 100644 index 0000000..5058ca1 --- /dev/null +++ b/src/tseng_clock.c @@ -0,0 +1,508 @@ + +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/tseng_clock.c,v 1.17 2001/02/15 17:54:55 eich Exp $ */ + + + + + +/* + * + * Copyright 1993-1997 The XFree86 Project, Inc. + * + */ +/** + ** Clock setting methods for Tseng chips + ** + ** The *ClockSelect() fucntions are ONLY used used for clock probing! + ** Setting the actual clock is done in TsengRestore(). + **/ + +#include "tseng.h" + +static Bool Tseng_ET4000ClockSelect(ScrnInfoPtr pScrn, int no); +static Bool Tseng_LegendClockSelect(ScrnInfoPtr pScrn, int no); + + +static SymTabRec TsengClockChips[] = +{ + {CLOCKCHIP_ICD2061A, "icd2061a"}, + {CLOCKCHIP_ET6000, "et6000"}, + {CLOCKCHIP_ICS5341, "ics5341"}, + {CLOCKCHIP_ICS5301, "ics5301"}, + {CLOCKCHIP_CH8398, "ch8398"}, + {CLOCKCHIP_STG1703, "stg1703"}, + {-1, NULL} +}; + +Bool +Tseng_check_clockchip(ScrnInfoPtr pScrn) +{ + MessageType from; + TsengPtr pTseng = TsengPTR(pScrn); + + PDEBUG(" Tseng_check_clockchip\n"); + + if (pTseng->pEnt->device->clockchip && *pTseng->pEnt->device->clockchip) { + /* clockchip given as a string in the config file */ + pScrn->clockchip = pTseng->pEnt->device->clockchip; + pTseng->ClockChip = xf86StringToToken(TsengClockChips, pScrn->clockchip); + if (pTseng->ClockChip == -1) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unknown clockchip: \"%s\"\n", + pScrn->clockchip); + return FALSE; + } + from = X_CONFIG; + } else { + /* ramdac probe already defined pTseng->ClockChip */ + pScrn->clockchip = (char *)xf86TokenToString(TsengClockChips, pTseng->ClockChip); + from = X_PROBED; + } + xf86DrvMsg(pScrn->scrnIndex, from, "Clockchip: \"%s\"\n", + pScrn->clockchip); + + return TRUE; +} + + +void tseng_clock_setup(ScrnInfoPtr pScrn) +{ + TsengPtr pTseng = TsengPTR(pScrn); + int iobase = VGAHW_GET_IOBASE(); + MessageType from; + int dacspeed, mem_bw; + Bool forceSpeed = FALSE; + int i; + + PDEBUG(" tseng_clock_setup\n"); + + /* + * Memory bandwidth is important in > 8bpp modes, especially on ET4000 + * + * This code evaluates a video mode with respect to requested dot clock + * (depends on the VGA chip and the RAMDAC) and the resulting bandwidth + * demand on memory (which in turn depends on color depth). + * + * For each mode, the minimum of max data transfer speed (dot clock + * limit) and memory bandwidth determines if the mode is allowed. + * + * We should also take acceleration into account: accelerated modes + * strain the bandwidth heavily, because they cause lots of random + * acesses to video memory, which is bad for bandwidth due to smaller + * page-mode memory requests. + */ + + /* Set the min pixel clock */ + pTseng->MinClock = 12000; /* XXX Guess, need to check this */ + + /* + * If the user has specified ramdac speed in the XF86Config + * file, we respect that setting. + */ + if (pTseng->pEnt->device->dacSpeeds[0]) { + from = X_CONFIG; + forceSpeed = TRUE; + switch (pScrn->bitsPerPixel) { + default: + case 1: + case 4: + case 8: + dacspeed = pTseng->pEnt->device->dacSpeeds[DAC_BPP8]; + break; + case 16: + dacspeed = pTseng->pEnt->device->dacSpeeds[DAC_BPP16]; + break; + case 24: + dacspeed = pTseng->pEnt->device->dacSpeeds[DAC_BPP24]; + break; + case 32: + dacspeed = pTseng->pEnt->device->dacSpeeds[DAC_BPP32]; + break; + } + pTseng->max_vco_freq = pTseng->pEnt->device->dacSpeeds[0]*2+1; + /* if a bpp-specific DacSpeed is not defined, use the "default" one (=8bpp) */ + if (dacspeed == 0) + dacspeed = pTseng->pEnt->device->dacSpeeds[0]; + } else { + from = X_PROBED; + forceSpeed = FALSE; + /* default */ + dacspeed = MAX_TSENG_CLOCK; + /* + * According to Tseng (about the ET6000): + * "Besides the 135 MHz maximum pixel clock frequency, the other limit has to + * do with where you get FIFO breakdown (usually appears as stray horizontal + * lines on the screen). Assuming the accelerator is running steadily doing a + * worst case operation, to avoid FIFO breakdown you should keep the product + * pixel_clock*(bytes/pixel) <= 225 MHz . This is based on an XCLK + * (system/memory) clock of 92 MHz (which is what we currently use) and + * a value in the RAS/CAS Configuration register (CFG 44) of either 015h + * or 014h (depending on the type of MDRAM chips). Also, the FIFO low + * threshold control bit (bit 4 of CFG 41) should be set for modes where + * pixel_clock*(bytes/pixel) > 130 MHz . These limits are for the + * current ET6000 chips. The ET6100 will raise the pixel clock limit + * to 175 MHz and the pixel_clock*(bytes/pixel) FIFO breakdown limit + * to about 275 MHz." + */ + if (Is_ET6100) { + dacspeed = 175000; + mem_bw = 280000; /* 275000 is _just_ not enough for 1152x864x24 @ 70Hz */ + } else if (Is_ET6000) { + dacspeed = 135000; + mem_bw = 225000; + } else { + if ( (pTseng->DacInfo.DacPort16) && + (pScrn->bitsPerPixel == 8) && + (!(DAC_is_GenDAC && pTseng->NoClockchip)) ) { + dacspeed = 135000; /* we can do PIXMUX */ + } + mem_bw = 90000; + if (pScrn->videoRam > 1024) + mem_bw = 150000; /* interleaved DRAM gives 70% more bandwidth */ + } + pTseng->max_vco_freq = dacspeed*2+1; + /* + * "dacspeed" is the theoretical limit imposed by the RAMDAC. + * "mem_bw" is the max memory bandwidth in mb/sec available + * for the pixel FIFO. + * The lowest of the two determines the actual pixel clock limit. + */ + dacspeed = min(dacspeed, (mem_bw / pTseng->Bytesperpixel)); + } + + /* + * Setup the ClockRanges, which describe what clock ranges are available, + * and what sort of modes they can be used for. + * + * First, we set up the default case, and modify it later if needed. + */ + pTseng->clockRange[0] = xnfcalloc(sizeof(ClockRange), 1); + pTseng->clockRange[0]->next = NULL; + pTseng->clockRange[0]->minClock = pTseng->MinClock; + pTseng->clockRange[0]->maxClock = dacspeed; + pTseng->clockRange[0]->clockIndex = -1; /* programmable -- not used */ + pTseng->clockRange[0]->interlaceAllowed = TRUE; + pTseng->clockRange[0]->doubleScanAllowed = TRUE; + pTseng->clockRange[0]->ClockMulFactor = 1; + pTseng->clockRange[0]->ClockDivFactor = 1; + pTseng->clockRange[0]->PrivFlags = TSENG_MODE_NORMAL; + + /* + * Handle PIXMUX modes. + * + * NOTE: We disable PIXMUX when clockchip programming on the GenDAC + * family is disabled. PIXMUX requires that the N2 post-divider in the + * PLL clock programming word is >= 2, which is not always true for the + * default (BIOS) clocks programmed in the 8 clock registers. + */ + if ( (pTseng->DacInfo.DacPort16) && + (pScrn->bitsPerPixel == 8) && + (!(DAC_is_GenDAC && pTseng->NoClockchip)) ) { + pTseng->clockRange[0]->maxClock = MAX_TSENG_CLOCK; + /* set up 2nd clock range for PIXMUX modes */ + pTseng->clockRange[1] = xnfcalloc(sizeof(ClockRange), 1); + pTseng->clockRange[0]->next = pTseng->clockRange[1]; + pTseng->clockRange[1]->next = NULL; + pTseng->clockRange[1]->minClock = 75000; + pTseng->clockRange[1]->maxClock = dacspeed; + pTseng->clockRange[1]->clockIndex = -1; /* programmable -- not used */ + pTseng->clockRange[1]->interlaceAllowed = TRUE; + pTseng->clockRange[1]->doubleScanAllowed = TRUE; + pTseng->clockRange[1]->ClockMulFactor = 1; + pTseng->clockRange[1]->ClockDivFactor = 2; + pTseng->clockRange[1]->PrivFlags = TSENG_MODE_PIXMUX; + } + + /* + * Handle 16/24/32 bpp modes that require some form of clock scaling. We + * can have either 8-bit DACs that require "bytesperpixel" clocks per + * pixel, or 16-bit DACs that can transport 8 or 16 bits per clock. + */ + if ((pTseng->Bytesperpixel > 1) && (!Is_ET6K)) { + /* in either 8 or 16-bit DAC case, we can use an 8-bit interface */ + pTseng->clockRange[0]->maxClock = (forceSpeed) ? dacspeed : + min(MAX_TSENG_CLOCK / pTseng->Bytesperpixel, dacspeed); + pTseng->clockRange[0]->ClockMulFactor = pTseng->Bytesperpixel; + pTseng->clockRange[0]->ClockDivFactor = 1; + /* in addition, 16-bit DACs can also transport 2 bytes per clock */ + if (pTseng->DacInfo.DacPort16) { + pTseng->clockRange[1] = xnfcalloc(sizeof(ClockRange), 1); + pTseng->clockRange[0]->next = pTseng->clockRange[1]; + pTseng->clockRange[1]->next = NULL; + pTseng->clockRange[1]->minClock = pTseng->MinClock; + pTseng->clockRange[1]->maxClock = (forceSpeed) ? dacspeed : + min((MAX_TSENG_CLOCK * 2) / pTseng->Bytesperpixel, dacspeed); + pTseng->clockRange[1]->clockIndex = -1; /* programmable -- not used */ + pTseng->clockRange[1]->interlaceAllowed = TRUE; + pTseng->clockRange[1]->doubleScanAllowed = TRUE; + pTseng->clockRange[1]->ClockMulFactor = pTseng->Bytesperpixel; + pTseng->clockRange[1]->ClockDivFactor = 2; + pTseng->clockRange[1]->PrivFlags = TSENG_MODE_DACBUS16; + } + } + + if (pTseng->clockRange[1]) + pTseng->MaxClock = pTseng->clockRange[1]->maxClock; + else + pTseng->MaxClock = pTseng->clockRange[0]->maxClock; + + xf86DrvMsg(pScrn->scrnIndex, X_DEFAULT, "Min pixel clock is %d MHz\n", + pTseng->MinClock / 1000); + xf86DrvMsg(pScrn->scrnIndex, from, "Max pixel clock is %d MHz\n", + pTseng->MaxClock / 1000); + + /* Memory clock setup */ + pTseng->MClkInfo.Set = FALSE; + /* Only set MemClk if appropriate for the ramdac */ + if (pTseng->MClkInfo.Programmable) { + from = X_PROBED; + if (pTseng->MemClk > 0) { + if ((pTseng->MemClk < pTseng->MClkInfo.min) + || (pTseng->MemClk > pTseng->MClkInfo.max)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "MCLK %d MHz out of range (=%d..%d); not changed!\n", + pTseng->MemClk / 1000, + pTseng->MClkInfo.min / 1000, + pTseng->MClkInfo.max / 1000); + } else { + pTseng->MClkInfo.MemClk = pTseng->MemClk; + pTseng->MClkInfo.Set = TRUE; + from = X_CONFIG; + } + } + xf86DrvMsg(pScrn->scrnIndex, from, "MCLK used is %d MHz\n", + pTseng->MClkInfo.MemClk / 1000); + } + + /* + * Set up the list-of-clocks stuff if we don't have a programmable + * clockchip (the RAMDAC probe sets the pScrn->progClock field). + */ + if (!pScrn->progClock) { + int NoClocks; + Bool (*TsengClockSelect)(ScrnInfoPtr, int); + + /* first determine how many clocks there are (or can be) */ + if (pTseng->Legend) { + TsengClockSelect = Tseng_LegendClockSelect; + NoClocks = 32; + } else { + TsengClockSelect = Tseng_ET4000ClockSelect; + /* + * The CH8398 RAMDAC uses CS3 for register selection (RS2), not for clock selection. + * The GenDAC family only has 8 clocks. Together with MCLK/2, that's 16 clocks. + */ + if ( (!Is_stdET4K) + && (!DAC_is_GenDAC) && (pTseng->DacInfo.DacType != CH8398_DAC) ) + NoClocks = 32; + else + NoClocks = 16; + } + /* now probe for the clocks if they are not specified */ + if (!pTseng->pEnt->device->numclocks) { + pScrn->numClocks = NoClocks; + xf86GetClocks(pScrn, NoClocks, TsengClockSelect, + TsengProtect, TsengBlankScreen, + iobase + 0x0A, 0x08, 1, 28322); + from = X_PROBED; + } else { + pScrn->numClocks = pTseng->pEnt->device->numclocks; + if (pScrn->numClocks > NoClocks) { + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, + "Too many Clocks specified in configuration file.\n"); + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, + "\t\tAt most %d clocks may be specified\n", NoClocks); + pScrn->numClocks= NoClocks; + } + for (i = 0; i < pScrn->numClocks; i++) + pScrn->clock[i] = pTseng->pEnt->device->clock[i]; + from = X_CONFIG; + } + /* + * Scale clocks for current bpp depending on RAMDAC type, and print + * out the list of clocks used. + */ + +#ifdef FIXME + for (i = 0; i < pScrn->numClocks; i++) { + pScrn->clock[i] *= pTseng->DacInfo.ClockDivFactor; + pScrn->clock[i] /= pTseng->DacInfo.ClockMulFactor; + } +#endif + xf86ShowClocks(pScrn, from); + } +} + + +/* + * ET4000ClockSelect -- + * select one of the possible clocks ... + */ + +static Bool +Tseng_ET4000ClockSelect(ScrnInfoPtr pScrn, int no) +{ + TsengPtr pTseng = TsengPTR(pScrn); + unsigned char temp; + int iobase = VGAHW_GET_IOBASE(); + + switch (no) { + case CLK_REG_SAVE: + pTseng->save_clock.save1 = inb(0x3CC); + outb(iobase + 4, 0x34); + pTseng->save_clock.save2 = inb(iobase + 5); + outb(0x3C4, 7); + pTseng->save_clock.save3 = inb(0x3C5); + if (!Is_stdET4K) { + outb(iobase + 4, 0x31); + pTseng->save_clock.save4 = inb(iobase + 5); + } + break; + case CLK_REG_RESTORE: + outb(0x3C2, pTseng->save_clock.save1); + outw(iobase + 4, 0x34 | (pTseng->save_clock.save2 << 8)); + outw(0x3C4, 7 | (pTseng->save_clock.save3 << 8)); + if (!Is_stdET4K) { + outw(iobase + 4, 0x31 | (pTseng->save_clock.save4 << 8)); + } + break; + default: + /* CS0,CS1 = clock select bits 0,1 */ + temp = inb(0x3CC); + outb(0x3C2, (temp & 0xf3) | ((no << 2) & 0x0C)); + /* CS2 = clock select bit 2 */ + outb(iobase + 4, 0x34); /* don't nuke the other bits in CR34 */ + temp = inb(iobase + 5); + outw(iobase + 4, 0x34 | ((temp & 0xFD) << 8) | ((no & 0x04) << 7)); + /* CS3 = clock select bit 4 */ + outb(0x3C4, 7); + temp = inb(0x3C5); + outb(0x3C5, (pTseng->save_divide ^ ((no & 0x8) << 3)) | (temp & 0xBF)); + /* CS4 = MCLK/2 */ + outb(iobase + 4, 0x31); + temp = inb(iobase + 5); + outb(iobase + 5, (temp & 0x3f) | ((no & 0x10) << 2)); + } + return (TRUE); +} + +/* + * LegendClockSelect -- + * select one of the possible clocks ... + */ + +static Bool +Tseng_LegendClockSelect(ScrnInfoPtr pScrn, int no) +{ + /* + * Sigma Legend special handling + * + * The Legend uses an ICS 1394-046 clock generator. This can generate 32 + * different frequencies. The Legend can use all 32. Here's how: + * + * There are two flip/flops used to latch two inputs into the ICS clock + * generator. The five inputs to the ICS are then + * + * ICS ET-4000 + * --- --- + * FS0 CS0 + * FS1 CS1 + * FS2 ff0 flip/flop 0 output + * FS3 CS2 + * FS4 ff1 flip/flop 1 output + * + * The flip/flops are loaded from CS0 and CS1. The flip/flops are + * latched by CS2, on the rising edge. After CS2 is set low, and then high, + * it is then set to its final value. + * + */ + TsengPtr pTseng = TsengPTR(pScrn); + unsigned char temp; + int iobase = VGAHW_GET_IOBASE(); + + switch (no) { + case CLK_REG_SAVE: + pTseng->save_clock.save1 = inb(0x3CC); + outb(iobase + 4, 0x34); + pTseng->save_clock.save2 = inb(iobase + 5); + break; + case CLK_REG_RESTORE: + outb(0x3C2, pTseng->save_clock.save1); + outw(iobase + 4, 0x34 | (pTseng->save_clock.save2 << 8)); + break; + default: + temp = inb(0x3CC); + outb(0x3C2, (temp & 0xF3) | ((no & 0x10) >> 1) | (no & 0x04)); + outw(iobase + 4, 0x0034); + outw(iobase + 4, 0x0234); + outw(iobase + 4, ((no & 0x08) << 6) | 0x34); + outb(0x3C2, (temp & 0xF3) | ((no << 2) & 0x0C)); + } + return (TRUE); +} + + +#define BASE_FREQ 14.31818 /* MHz */ +void +TsengcommonCalcClock(long freq, int min_m, int min_n1, int max_n1, int min_n2, int max_n2, + long freq_min, long freq_max, + unsigned char *mdiv, unsigned char *ndiv) +{ + double ffreq, ffreq_min, ffreq_max; + double div, diff, best_diff; + unsigned int m; + unsigned char n1, n2; + unsigned char best_n1 = 16 + 2, best_n2 = 2, best_m = 125 + 2; + + PDEBUG(" commonCalcClock\n"); + ffreq = freq / 1000.0 / BASE_FREQ; + ffreq_min = freq_min / 1000.0 / BASE_FREQ; + ffreq_max = freq_max / 1000.0 / BASE_FREQ; + + if (ffreq < ffreq_min / (1 << max_n2)) { + ErrorF("invalid frequency %1.3f MHz [freq >= %1.3f MHz]\n", + ffreq * BASE_FREQ, ffreq_min * BASE_FREQ / (1 << max_n2)); + ffreq = ffreq_min / (1 << max_n2); + } + if (ffreq > ffreq_max / (1 << min_n2)) { + ErrorF("invalid frequency %1.3f MHz [freq <= %1.3f MHz]\n", + ffreq * BASE_FREQ, ffreq_max * BASE_FREQ / (1 << min_n2)); + ffreq = ffreq_max / (1 << min_n2); + } + /* work out suitable timings */ + + best_diff = ffreq; + + for (n2 = min_n2; n2 <= max_n2; n2++) { + for (n1 = min_n1 + 2; n1 <= max_n1 + 2; n1++) { + m = (int)(ffreq * n1 * (1 << n2) + 0.5); + if (m < min_m + 2 || m > 127 + 2) + continue; + div = (double)(m) / (double)(n1); + if ((div >= ffreq_min) && + (div <= ffreq_max)) { + diff = ffreq - div / (1 << n2); + if (diff < 0.0) + diff = -diff; + if (diff < best_diff) { + best_diff = diff; + best_m = m; + best_n1 = n1; + best_n2 = n2; + } + } + } + } + +#ifdef EXTENDED_DEBUG + ErrorF("Clock parameters for %1.6f MHz: m=%d, n1=%d, n2=%d\n", + ((double)(best_m) / (double)(best_n1) / (1 << best_n2)) * BASE_FREQ, + best_m - 2, best_n1 - 2, best_n2); +#endif + + if (max_n1 == 63) + *ndiv = (best_n1 - 2) | (best_n2 << 6); + else + *ndiv = (best_n1 - 2) | (best_n2 << 5); + *mdiv = best_m - 2; +} + diff --git a/src/tseng_colexp.c b/src/tseng_colexp.c new file mode 100644 index 0000000..9c471a9 --- /dev/null +++ b/src/tseng_colexp.c @@ -0,0 +1,543 @@ +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/tseng_colexp.c,v 1.14 2001/02/15 17:54:55 eich Exp $ */ + + + + + +/* + * ET4/6K acceleration interface -- color expansion primitives. + * + * Uses Harm Hanemaayer's generic acceleration interface (XAA). + * + * Author: Koen Gadeyne + * + * Much of the acceleration code is based on the XF86_W32 server code from + * Glenn Lai. + * + * + * Color expansion capabilities of the Tseng chip families: + * + * Chip screen-to-screen CPU-to-screen Supported depths + * + * ET4000W32/W32i No Yes 8bpp only + * ET4000W32p Yes Yes 8bpp only + * ET6000 Yes No 8/16/24/32 bpp + */ + +#include "tseng.h" +#include "tseng_acl.h" +#include "tseng_inline.h" + +void TsengSetupForScreenToScreenColorExpandFill(ScrnInfoPtr pScrn, + int fg, int bg, int rop, unsigned int planemask); + +void TsengSubsequentScreenToScreenColorExpandFill(ScrnInfoPtr pScrn, + int x, int y, int w, int h, int srcx, int srcy, int skipleft); + +void TsengSubsequentScanlineCPUToScreenColorExpandFill(ScrnInfoPtr pScrn, + int x, int y, int w, int h, int skipleft); + +void TsengSubsequentColorExpandScanline(ScrnInfoPtr pScrn, + int bufno); + +void TsengSetupForCPUToScreenColorExpandFill(ScrnInfoPtr pScrn, + int fg, int bg, int rop, unsigned int planemask); + +void TsengSubsequentCPUToScreenColorExpandFill(ScrnInfoPtr pScrn, + int x, int y, int w, int h, int skipleft); + +void TsengSubsequentColorExpandScanline_8bpp(ScrnInfoPtr pScrn, int bufno); +void TsengSubsequentColorExpandScanline_16bpp(ScrnInfoPtr pScrn, int bufno); +void TsengSubsequentColorExpandScanline_24bpp(ScrnInfoPtr pScrn, int bufno); +void TsengSubsequentColorExpandScanline_32bpp(ScrnInfoPtr pScrn, int bufno); + +Bool +TsengXAAInit_Colexp(ScrnInfoPtr pScrn) +{ + int i, j, r; + TsengPtr pTseng = TsengPTR(pScrn); + XAAInfoRecPtr pXAAInfo = pTseng->AccelInfoRec; + + PDEBUG(" TsengXAAInit_Colexp\n"); + +#ifdef TODO + if (OFLG_ISSET(OPTION_XAA_NO_COL_EXP, &vga256InfoRec.options)) + return; +#endif + + /* FIXME! disable accelerated color expansion for W32/W32i until it's fixed */ +/* if (Is_W32 || Is_W32i) return; */ + + /* + * Screen-to-screen color expansion. + * + * Scanline-screen-to-screen color expansion is slower than + * CPU-to-screen color expansion. + */ + + pXAAInfo->ScreenToScreenColorExpandFillFlags = + BIT_ORDER_IN_BYTE_LSBFIRST | + SCANLINE_PAD_DWORD | + LEFT_EDGE_CLIPPING | + NO_PLANEMASK; + +#if 1 + if (Is_ET6K || (Is_W32p && (pScrn->bitsPerPixel == 8))) { + pXAAInfo->SetupForScreenToScreenColorExpandFill = + TsengSetupForScreenToScreenColorExpandFill; + pXAAInfo->SubsequentScreenToScreenColorExpandFill = + TsengSubsequentScreenToScreenColorExpandFill; + } +#endif + + /* + * Scanline CPU to screen color expansion for all W32 engines. + * + * real CPU-to-screen color expansion is extremely tricky, and only + * works for 8bpp anyway. + * + * This also allows us to do 16, 24 and 32 bpp color expansion by first + * doubling the bitmap pattern before color-expanding it, because W32s + * can only do 8bpp color expansion. + */ + + pXAAInfo->ScanlineCPUToScreenColorExpandFillFlags = + BIT_ORDER_IN_BYTE_LSBFIRST | + SCANLINE_PAD_DWORD | + NO_PLANEMASK; + +#if 1 + if (!Is_ET6K) { + pTseng->XAAScanlineColorExpandBuffers[0] = + xnfalloc(((pScrn->virtualX + 31)/32) * 4 * pTseng->Bytesperpixel); + if (pTseng->XAAScanlineColorExpandBuffers[0] == NULL) { + xf86Msg(X_ERROR, "Could not malloc color expansion scanline buffer.\n"); + return FALSE; + } + pXAAInfo->NumScanlineColorExpandBuffers = 1; + pXAAInfo->ScanlineColorExpandBuffers = pTseng->XAAScanlineColorExpandBuffers; + + pXAAInfo->SetupForScanlineCPUToScreenColorExpandFill = + TsengSetupForCPUToScreenColorExpandFill; + + pXAAInfo->SubsequentScanlineCPUToScreenColorExpandFill = + TsengSubsequentScanlineCPUToScreenColorExpandFill; + + switch (pScrn->bitsPerPixel) { + case 8: + pXAAInfo->SubsequentColorExpandScanline = + TsengSubsequentColorExpandScanline_8bpp; + break; + case 15: + case 16: + pXAAInfo->SubsequentColorExpandScanline = + TsengSubsequentColorExpandScanline_16bpp; + break; + case 24: + pXAAInfo->SubsequentColorExpandScanline = + TsengSubsequentColorExpandScanline_24bpp; + break; + case 32: + pXAAInfo->SubsequentColorExpandScanline = + TsengSubsequentColorExpandScanline_32bpp; + break; + } + /* create color expansion LUT (used for >8bpp only) */ + pTseng->ColExpLUT = xnfalloc(sizeof(CARD32)*256); + if (pTseng->ColExpLUT == NULL) { + xf86Msg(X_ERROR, "Could not malloc color expansion tables.\n"); + return FALSE; + } + for (i = 0; i < 256; i++) { + r = 0; + for (j = 7; j >= 0; j--) { + r <<= pTseng->Bytesperpixel; + if ((i >> j) & 1) + r |= (1 << pTseng->Bytesperpixel) - 1; + } + pTseng->ColExpLUT[i] = r; + /* ErrorF("0x%08X, ",r ); if ((i%8)==7) ErrorF("\n"); */ + } + } +#endif +#if 1 + if (Is_ET6K) { + /* + * Triple-buffering is needed to account for double-buffering of Tseng + * acceleration registers. + */ + pXAAInfo->NumScanlineColorExpandBuffers = 3; + pXAAInfo->ScanlineColorExpandBuffers = + pTseng->XAAColorExpandBuffers; + pXAAInfo->SetupForScanlineCPUToScreenColorExpandFill = + TsengSetupForScreenToScreenColorExpandFill; + pXAAInfo->SubsequentScanlineCPUToScreenColorExpandFill = + TsengSubsequentScanlineCPUToScreenColorExpandFill; + pXAAInfo->SubsequentColorExpandScanline = + TsengSubsequentColorExpandScanline; + + /* calculate memory addresses from video memory offsets */ + for (i = 0; i < pXAAInfo->NumScanlineColorExpandBuffers; i++) { + pTseng->XAAColorExpandBuffers[i] = + pTseng->FbBase + pTseng->AccelColorExpandBufferOffsets[i]; + } + + /* + * for banked memory, translate those addresses to fall in the + * correct aperture. Color expansion uses aperture #0, which sits at + * pTseng->FbBase + 0x18000 + 48. + */ + if (!pTseng->UseLinMem) { + for (i = 0; i < pXAAInfo->NumScanlineColorExpandBuffers; i++) { + pTseng->XAAColorExpandBuffers[i] = + pTseng->XAAColorExpandBuffers[i] + - pTseng->AccelColorExpandBufferOffsets[0] + + 0x18000 + 48; + } + } + pXAAInfo->ScanlineColorExpandBuffers = pTseng->XAAColorExpandBuffers; + } +#endif + +#ifdef TSENG_CPU_TO_SCREEN_COLOREXPAND + /* + * CPU-to-screen color expansion doesn't seem to be reliable yet. The + * W32 needs the correct amount of data sent to it in this mode, or it + * hangs the machine until is does (?). Currently, the init code in this + * file or the XAA code that uses this does something wrong, so that + * occasionally we get accelerator timeouts, and after a few, complete + * system hangs. + * + * The W32 engine requires SCANLINE_NO_PAD, but that doesn't seem to + * work very well (accelerator hangs). + * + * What works is this: tell XAA that we have SCANLINE_PAD_DWORD, and then + * add the following code in TsengSubsequentCPUToScreenColorExpand(): + * w = (w + 31) & ~31; this code rounds the width up to the nearest + * multiple of 32, and together with SCANLINE_PAD_DWORD, this makes + * CPU-to-screen color expansion work. Of course, the display isn't + * correct (4 chars are "blanked out" when only one is written, for + * example). But this shows that the principle works. But the code + * doesn't... + * + * The same thing goes for PAD_BYTE: this also works (with the same + * problems as SCANLINE_PAD_DWORD, although less prominent) + */ + + pXAAInfo->CPUToScreenColorExpandFillFlags = + BIT_ORDER_IN_BYTE_LSBFIRST | + SCANLINE_PAD_DWORD | /* no other choice */ + CPU_TRANSFER_PAD_DWORD | + NO_PLANEMASK; + + if (Is_W32_any && (pScrn->bitsPerPixel == 8)) { + pXAAInfo->SetupForCPUToScreenColorExpandFill = + TsengSetupForCPUToScreenColorExpandFill; + pXAAInfo->SubsequentCPUToScreenColorExpandFill = + TsengSubsequentCPUToScreenColorExpandFill; + + /* we'll be using MMU aperture 2 */ + pXAAInfo->ColorExpandBase = (CARD8 *)pTseng->tsengCPU2ACLBase; + /* ErrorF("tsengCPU2ACLBase = 0x%x\n", pTseng->tsengCPU2ACLBase); */ + /* aperture size is 8kb in banked mode. Larger in linear mode, but 8kb is enough */ + pXAAInfo->ColorExpandRange = 8192; + } +#endif + return TRUE; +} + +#define SET_FUNCTION_COLOREXPAND \ + if (Is_ET6K) \ + ACL_MIX_CONTROL(0x32); \ + else \ + ACL_ROUTING_CONTROL(0x08); + +#define SET_FUNCTION_COLOREXPAND_CPU \ + ACL_ROUTING_CONTROL(0x02); + + +void +TsengSubsequentScanlineCPUToScreenColorExpandFill(ScrnInfoPtr pScrn, + int x, int y, int w, int h, int skipleft) +{ + TsengPtr pTseng = TsengPTR(pScrn); + + if (!Is_ET6K) { + /* the accelerator needs DWORD padding, and "w" is in PIXELS... */ + pTseng->acl_colexp_width_dwords = (MULBPP(pTseng, w) + 31) >> 5; + pTseng->acl_colexp_width_bytes = (MULBPP(pTseng, w) + 7) >> 3; + } + + pTseng->acl_ColorExpandDst = FBADDR(pTseng, x, y); + pTseng->acl_skipleft = skipleft; + + wait_acl_queue(pTseng); + +#if 0 + ACL_MIX_Y_OFFSET(w - 1); + + ErrorF(" W=%d", w); +#endif + SET_XY(pTseng, w, 1); +} + +void +TsengSubsequentColorExpandScanline(ScrnInfoPtr pScrn, + int bufno) +{ + TsengPtr pTseng = TsengPTR(pScrn); + + wait_acl_queue(pTseng); + + ACL_MIX_ADDRESS((pTseng->AccelColorExpandBufferOffsets[bufno] << 3) + pTseng->acl_skipleft); + START_ACL(pTseng, pTseng->acl_ColorExpandDst); + + /* move to next scanline */ + pTseng->acl_ColorExpandDst += pTseng->line_width; + + /* + * If not using triple-buffering, we need to wait for the queued + * register set to be transferred to the working register set here, + * because otherwise an e.g. double-buffering mechanism could overwrite + * the buffer that's currently being worked with with new data too soon. + * + * WAIT_QUEUE; // not needed with triple-buffering + */ +} + + + +/* + * We use this intermediate CPU-to-Screen color expansion because the one + * provided by XAA seems to lock up the accelerator engine. + * + * One of the main differences between the XAA approach and this one is that + * transfers are done per byte. I'm not sure if that is needed though. + */ + +void TsengSubsequentColorExpandScanline_8bpp(ScrnInfoPtr pScrn, int bufno) +{ + TsengPtr pTseng = TsengPTR(pScrn); + pointer dest = pTseng->tsengCPU2ACLBase; + int i,j; + CARD8 *bufptr; + + i = pTseng->acl_colexp_width_bytes; + bufptr = (CARD8 *) (pTseng->XAAScanlineColorExpandBuffers[bufno]); + + wait_acl_queue(pTseng); + START_ACL (pTseng, pTseng->acl_ColorExpandDst); + +/* *((LongP) (MMioBase + 0x08)) = (CARD32) pTseng->acl_ColorExpandDst;*/ +/* MMIO_OUT32(tsengCPU2ACLBase,0, (CARD32)pTseng->acl_ColorExpandDst); */ + j = 0; + /* Copy scanline data to accelerator MMU aperture byte by byte */ + while (i--) { /* FIXME: we need to take care of PCI bursting and MMU overflow here! */ + MMIO_OUT8(dest,j++, *bufptr++); + } + + /* move to next scanline */ + pTseng->acl_ColorExpandDst += pTseng->line_width; +} + +/* + * This function does direct memory-to-CPU bit doubling for color-expansion + * at 16bpp on W32 chips. They can only do 8bpp color expansion, so we have + * to expand the incoming data to 2bpp first. + */ + +void TsengSubsequentColorExpandScanline_16bpp(ScrnInfoPtr pScrn, int bufno) +{ + TsengPtr pTseng = TsengPTR(pScrn); + pointer dest = pTseng->tsengCPU2ACLBase; + int i,j; + CARD8 *bufptr; + register CARD32 bits16; + + i = pTseng->acl_colexp_width_dwords * 2; + bufptr = (CARD8 *) (pTseng->XAAScanlineColorExpandBuffers[bufno]); + + wait_acl_queue(pTseng); + START_ACL(pTseng, pTseng->acl_ColorExpandDst); + + j = 0; + while (i--) { + bits16 = pTseng->ColExpLUT[*bufptr++]; + MMIO_OUT8(dest,j++,bits16 & 0xFF); + MMIO_OUT8(dest,j++,(bits16 >> 8) & 0xFF); + } + + /* move to next scanline */ + pTseng->acl_ColorExpandDst += pTseng->line_width; +} + +/* + * This function does direct memory-to-CPU bit doubling for color-expansion + * at 24bpp on W32 chips. They can only do 8bpp color expansion, so we have + * to expand the incoming data to 3bpp first. + */ + +void TsengSubsequentColorExpandScanline_24bpp(ScrnInfoPtr pScrn, int bufno) +{ + TsengPtr pTseng = TsengPTR(pScrn); + pointer dest = pTseng->tsengCPU2ACLBase; + int i, k, j = -1; + CARD8 *bufptr; + register CARD32 bits24; + + i = pTseng->acl_colexp_width_dwords * 4; + bufptr = (CARD8 *) (pTseng->XAAScanlineColorExpandBuffers[bufno]); + + wait_acl_queue(pTseng); + START_ACL(pTseng, pTseng->acl_ColorExpandDst); + + /* take 8 input bits, expand to 3 output bytes */ + bits24 = pTseng->ColExpLUT[*bufptr++]; + k = 0; + while (i--) { + if ((j++) == 2) { /* "i % 3" operation is much to expensive */ + j = 0; + bits24 = pTseng->ColExpLUT[*bufptr++]; + } + MMIO_OUT8(dest,k++,bits24 & 0xFF); + bits24 >>= 8; + } + + /* move to next scanline */ + pTseng->acl_ColorExpandDst += pTseng->line_width; +} + +/* + * This function does direct memory-to-CPU bit doubling for color-expansion + * at 32bpp on W32 chips. They can only do 8bpp color expansion, so we have + * to expand the incoming data to 4bpp first. + */ + +void TsengSubsequentColorExpandScanline_32bpp(ScrnInfoPtr pScrn, int bufno) +{ + TsengPtr pTseng = TsengPTR(pScrn); + pointer dest = pTseng->tsengCPU2ACLBase; + int i,j; + CARD8 *bufptr; + register CARD32 bits32; + + i = pTseng->acl_colexp_width_dwords; + /* amount of blocks of 8 bits to expand to 32 bits (=1 DWORD) */ + bufptr = (CARD8 *) (pTseng->XAAScanlineColorExpandBuffers[bufno]); + + wait_acl_queue(pTseng); + START_ACL(pTseng, pTseng->acl_ColorExpandDst); + + j = 0; + while (i--) { + bits32 = pTseng->ColExpLUT[*bufptr++]; + MMIO_OUT8(dest,j++,bits32 & 0xFF); + MMIO_OUT8(dest,j++,(bits32 >> 8) & 0xFF); + MMIO_OUT8(dest,j++,(bits32 >> 16) & 0xFF); + MMIO_OUT8(dest,j++,(bits32 >> 24) & 0xFF); + } + + /* move to next scanline */ + pTseng->acl_ColorExpandDst += pTseng->line_width; +} + +/* + * CPU-to-Screen color expansion. + * This is for ET4000 only (The ET6000 cannot do this) + */ + +void TsengSetupForCPUToScreenColorExpandFill(ScrnInfoPtr pScrn, + int fg, int bg, int rop, unsigned int planemask) +{ + TsengPtr pTseng = TsengPTR(pScrn); + +/* ErrorF("X"); */ + + PINGPONG(pTseng); + + wait_acl_queue(pTseng); + + SET_FG_ROP(rop); + SET_BG_ROP_TR(rop, bg); + + SET_XYDIR(0); + + SET_FG_BG_COLOR(pTseng, fg, bg); + + SET_FUNCTION_COLOREXPAND_CPU; + + /* assure correct alignment of MIX address (ACL needs same alignment here as in MMU aperture) */ + ACL_MIX_ADDRESS(0); +} + +/* + * TsengSubsequentCPUToScreenColorExpand() is potentially dangerous: + * Not writing enough data to the MMU aperture for CPU-to-screen color + * expansion will eventually cause a system deadlock! + * + * Note that CPUToScreenColorExpand operations _always_ require a + * WAIT_INTERFACE before starting a new operation (this is empyrical, + * though) + */ + +void TsengSubsequentCPUToScreenColorExpandFill(ScrnInfoPtr pScrn, + int x, int y, int w, int h, int skipleft) +{ + TsengPtr pTseng = TsengPTR(pScrn); + int destaddr = FBADDR(pTseng, x, y); + + /* ErrorF(" %dx%d|%d ",w,h,skipleft); */ + if (skipleft) + ErrorF("Can't do: Skipleft = %d\n", skipleft); + +/* wait_acl_queue(); */ + ErrorF("=========WAIT FIXME!\n"); + WAIT_INTERFACE; + + ACL_MIX_Y_OFFSET(w - 1); + SET_XY(pTseng, w, h); + START_ACL(pTseng, destaddr); +} + + +void +TsengSetupForScreenToScreenColorExpandFill(ScrnInfoPtr pScrn, + int fg, int bg, int rop, unsigned int planemask) +{ + TsengPtr pTseng = TsengPTR(pScrn); + +/* ErrorF("SSC "); */ + + PINGPONG(pTseng); + + wait_acl_queue(pTseng); + + SET_FG_ROP(rop); + SET_BG_ROP_TR(rop, bg); + + SET_FG_BG_COLOR(pTseng, fg, bg); + + SET_FUNCTION_COLOREXPAND; + + SET_XYDIR(0); +} + +void +TsengSubsequentScreenToScreenColorExpandFill(ScrnInfoPtr pScrn, + int x, int y, int w, int h, int srcx, int srcy, int skipleft) +{ + TsengPtr pTseng = TsengPTR(pScrn); + int destaddr = FBADDR(pTseng, x, y); + +/* int srcaddr = FBADDR(pTseng, srcx, srcy); */ + + wait_acl_queue(pTseng); + + SET_XY(pTseng, w, h); + ACL_MIX_ADDRESS( /* MIX address is in BITS */ + (((srcy * pScrn->displayWidth) + srcx) * pScrn->bitsPerPixel) + skipleft); + + ACL_MIX_Y_OFFSET(pTseng->line_width << 3); + + START_ACL(pTseng, destaddr); +} diff --git a/src/tseng_cursor.c b/src/tseng_cursor.c new file mode 100644 index 0000000..199082b --- /dev/null +++ b/src/tseng_cursor.c @@ -0,0 +1,270 @@ +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/tseng_cursor.c,v 1.17 2001/05/07 21:59:07 tsi Exp $ */ + + + + + +#include "tseng.h" + +static void TsengShowCursor(ScrnInfoPtr pScrn); +static void TsengHideCursor(ScrnInfoPtr pScrn); +static void TsengSetCursorPosition(ScrnInfoPtr pScrn, int x, int y); +static Bool TsengUseHWCursor(ScreenPtr pScreen, CursorPtr pCurs); +static void TsengSetCursorColors(ScrnInfoPtr pScrn, int bg, int fg); +static void TsengLoadCursorImage(ScrnInfoPtr pScrn, unsigned char *bits); + +Bool +TsengHWCursorInit(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + TsengPtr pTseng = TsengPTR(pScrn); + xf86CursorInfoPtr infoPtr; + + PDEBUG(" TsengHWCursorInit\n"); + + if (!pTseng->HWCursor) + return FALSE; + + infoPtr = xf86CreateCursorInfoRec(); + if (!infoPtr) + return FALSE; + + pTseng->CursorInfoRec = infoPtr; + + /* calculate memory addres from video memory offsets */ + pTseng->HWCursorBuffer = + pTseng->FbBase + pTseng->HWCursorBufferOffset; + + /* + * for banked memory, translate this address to fall in the + * correct aperture. HWcursor uses aperture #0, which sits at + * pTseng->FbBase + 0x18000. + */ + if (!pTseng->UseLinMem) { +#ifdef TODO + pTseng->HWCursorBuffer = + pTseng->something + - pTseng->what + + 0x18000; +#else + ErrorF("banked HW cursor not implemented yet!\n"); +#endif + } + + /* set up the XAA HW cursor structure */ + infoPtr->MaxWidth = 64; + infoPtr->MaxHeight = 64; + infoPtr->Flags = + HARDWARE_CURSOR_TRUECOLOR_AT_8BPP | + HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1 | + HARDWARE_CURSOR_AND_SOURCE_WITH_MASK | + HARDWARE_CURSOR_INVERT_MASK; + infoPtr->SetCursorColors = TsengSetCursorColors; + infoPtr->SetCursorPosition = TsengSetCursorPosition; + infoPtr->LoadCursorImage = TsengLoadCursorImage; + infoPtr->HideCursor = TsengHideCursor; + infoPtr->ShowCursor = TsengShowCursor; + infoPtr->UseHWCursor = TsengUseHWCursor; + + return (xf86InitCursor(pScreen, infoPtr)); +} + +static Bool +TsengUseHWCursor(ScreenPtr pScreen, CursorPtr pCurs) +{ + /* have this return false for DoubleScan and Interlaced ? */ + return TRUE; +} + +static void +TsengShowCursor(ScrnInfoPtr pScrn) +{ + unsigned char tmp; + TsengPtr pTseng = TsengPTR(pScrn); + + /* Enable the hardware cursor. */ + if (Is_ET6K) { + tmp = inb(pTseng->IOAddress + 0x46); + outb(pTseng->IOAddress + 0x46, (tmp | 0x01)); + } else { + outb(0x217A, 0xF7); + tmp = inb(0x217B); + outb(0x217B, tmp | 0x80); + } +} + +static void +TsengHideCursor(ScrnInfoPtr pScrn) +{ + unsigned char tmp; + TsengPtr pTseng = TsengPTR(pScrn); + + /* Disable the hardware cursor. */ + if (Is_ET6K) { + tmp = inb(pTseng->IOAddress + 0x46); + outb(pTseng->IOAddress + 0x46, (tmp & 0xfe));; + } else { + outb(0x217A, 0xF7); + tmp = inb(0x217B); + outb(0x217B, tmp & ~0x80); + } +} + +static void +TsengSetCursorPosition(ScrnInfoPtr pScrn, int x, int y) +{ + int xorigin, yorigin; + TsengPtr pTseng = TsengPTR(pScrn); + + /* + * If the cursor is partly out of screen at the left or top, + * we need to modify the origin. + */ + xorigin = 0; + yorigin = 0; + if (x < 0) { + xorigin = -x; + x = 0; + } + if (y < 0) { + yorigin = -y; + y = 0; + } +#ifdef TODO + /* Correct cursor position in DoubleScan modes */ + if (XF86SCRNINFO(pScr)->modes->Flags & V_DBLSCAN) + y *= 2; +#endif + + if (Is_ET6K) { + outb(pTseng->IOAddress + 0x82, xorigin); + outb(pTseng->IOAddress + 0x83, yorigin); + + outb(pTseng->IOAddress + 0x84, (x & 0xff)); /* X bits 7-0 */ + outb(pTseng->IOAddress + 0x85, ((x >> 8) & 0x0f)); /* X bits 11-8 */ + + outb(pTseng->IOAddress + 0x86, (y & 0xff)); /* Y bits 7-0 */ + outb(pTseng->IOAddress + 0x87, ((y >> 8) & 0x0f)); /* Y bits 11-8 */ + } else { + outb(0x217A, 0xE2); + outb(0x217B, xorigin); + outb(0x217A, 0xE6); + outb(0x217B, yorigin); + + outb(0x217A, 0xE0); + outb(0x217B, (x & 0xff)); /* X bits 7-0 */ + outb(0x217A, 0xE1); + outb(0x217B, ((x >> 8) & 0x0f)); /* X bits 10-8 */ + + outb(0x217A, 0xE4); + outb(0x217B, (y & 0xff)); /* Y bits 7-0 */ + outb(0x217A, 0xE5); + outb(0x217B, ((y >> 8) & 0x0f)); /* Y bits 10-8 */ + } +} + +/* + * The ET6000 cursor color is only 6 bits, with 2 bits per color. This + * is of course very inaccurate, but high-bit-depth color differences + * are only visible on _large_ planes of equal color. i.e. small areas + * of a certain color (like a cursor) don't need many bits per pixel at + * all, because the difference will not be seen. + * + * So it won't be as bad, but should still be documented nonetheless. + */ +static void +TsengSetCursorColors(ScrnInfoPtr pScrn, int bg, int fg) +{ + TsengPtr pTseng = TsengPTR(pScrn); + unsigned char et6k_fg, et6k_bg; + + if (Is_ET6K) { + et6k_fg = (fg & 0x00000003) + | ((fg & 0x00000300) >> 6) + | ((fg & 0x00030000) >> 12); + et6k_bg = (bg & 0x00000003) + | ((bg & 0x00000300) >> 6) + | ((bg & 0x00030000) >> 12); + + outb(pTseng->IOAddress + 0x67, 0x09); /* prepare for colour data */ + outb(pTseng->IOAddress + 0x69, et6k_bg); + outb(pTseng->IOAddress + 0x69, et6k_fg); + } else { + /* + * The ET4000 uses color 0 as sprite color "0", and color 0xFF as + * sprite color "1". Changing colors implies changing colors 0 and + * 255. This is currently not implemented. + * + * In non-8bpp modes, this would result in always black and white + * colors (since the colormap isn't there to translate 0 and 255 to + * other colors). And besides, in non-8bpp, there seem to be TWO + * cursor images on the screen... + */ + xf86Msg(X_ERROR, "Internal error: ET4000 hardware cursor color changes not implemented\n"); + } +} + +void +TsengLoadCursorImage(ScrnInfoPtr pScrn, unsigned char *bits) +{ + TsengPtr pTseng = TsengPTR(pScrn); + + int iobase = VGAHW_GET_IOBASE(); + unsigned char tmp; +#ifdef DEBUG_HWC + int i; + int d; + + for (i = 0; i < 1024; i++) { + d = *(bits + i); + ErrorF("%d%d%d%d", d & 0x03, (d >> 2) & 0x03, (d >> 4) & 0x03, (d >> 6) & 0x03); + if ((i & 15) == 15) + ErrorF("\n"); + } +#endif + /* + * Program the cursor image address in video memory. + * We need to set it here or we might loose it on mode/vt switches. + */ + + if (Is_ET6K) { + /* bits 19:16 */ + outb(iobase + 0x04, 0x0E); + tmp = inb(iobase + 0x05) & 0xF0; + outb(iobase + 0x05, tmp | (((pTseng->HWCursorBufferOffset / 4) >> 16) & 0x0F)); + /* bits 15:8 */ + outb(iobase + 0x04, 0x0F); + outb(iobase + 0x05, ((pTseng->HWCursorBufferOffset / 4) >> 8) & 0xFF); + /* on the ET6000, bits (7:0) are always 0 */ + } else { + /* bits 19:16 */ + outb(0x217A, 0xEA); + tmp = inb(0x217B) & 0xF0; + outb(0x217B, tmp | (((pTseng->HWCursorBufferOffset / 4) >> 16) & 0x0F)); + /* bits 15:8 */ + outb(0x217A, 0xE9); + outb(0x217B, ((pTseng->HWCursorBufferOffset / 4) >> 8) & 0xFF); + /* bits 7:0 */ + outb(0x217A, 0xE8); + outb(0x217B, (pTseng->HWCursorBufferOffset / 4) & 0xFF); + + /* this needs to be set for the sprite */ + outb(0x217A, 0xEB); + outb(0x217B, 2); + outb(0x217A, 0xEC); + tmp = inb(0x217B); + outb(0x217B, tmp & 0xFE); + outb(0x217A, 0xEF); + tmp = inb(0x217B); + outb(0x217B, (tmp & 0xF8) | 0x02); + outb(0x217A, 0xEE); + outb(0x217B, 1); + } + /* this assumes the apertures have been set up correctly for banked mode */ + memcpy(pTseng->HWCursorBuffer, bits, 1024); +} + + + + + diff --git a/src/tseng_dga.c b/src/tseng_dga.c new file mode 100644 index 0000000..4623f08 --- /dev/null +++ b/src/tseng_dga.c @@ -0,0 +1,249 @@ +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/tseng_dga.c,v 1.2 2001/10/01 13:44:11 eich Exp $ */ +/* + * Copyright 2000 by Rainer Keller, <Rainer.Keller@studmail.uni-stuttgart.de>. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Alan Hourihane not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Alan Hourihane makes no representations + * about the suitability of this software for any purpose. It is provided + * "as is" without express or implied warranty. + * + * ALAN HOURIHANE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL ALAN HOURIHANE BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Authors: Adapted from: Alan Hourihane, <alanh@fairlite.demon.co.uk> + * by: Rainer Keller, <Rainer.Keller@studmail.uni-stuttgart.de> + */ + +#include "tseng.h" +#include "dgaproc.h" + +static Bool Tseng_OpenFramebuffer(ScrnInfoPtr, char **, unsigned char **, + int *, int *, int *); +static Bool Tseng_SetMode(ScrnInfoPtr, DGAModePtr); +static void Tseng_Sync(ScrnInfoPtr); +static int Tseng_GetViewport(ScrnInfoPtr); +static void Tseng_SetViewport(ScrnInfoPtr, int, int, int); +static void Tseng_FillRect(ScrnInfoPtr, int, int, int, int, unsigned long); +static void Tseng_BlitRect(ScrnInfoPtr, int, int, int, int, int, int); +/* +static void Tseng_BlitTransRect(ScrnInfoPtr, int, int, int, int, int, int, + unsigned long); +*/ + +static +DGAFunctionRec TsengDGAFuncs = { + Tseng_OpenFramebuffer, + NULL, /* Tseng_CloseFramebuffer */ + Tseng_SetMode, + Tseng_SetViewport, + Tseng_GetViewport, + Tseng_Sync, + Tseng_FillRect, + Tseng_BlitRect, + NULL /* Tseng_BlitTransRect */ +}; + + + + +Bool +TsengDGAInit(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + TsengPtr pTseng = TsengPTR(pScrn); + DGAModePtr modes = NULL, newmodes = NULL, currentMode; + DisplayModePtr pMode, firstMode; + int Bpp = pScrn->bitsPerPixel >> 3; + int num = 0; + int imlines = (pScrn->videoRam * 1024) / + (pScrn->displayWidth * (pScrn->bitsPerPixel >> 3)); + + + if (!pTseng->UseLinMem) + return FALSE; + + if (!pTseng->DGAnumModes) { + pMode = firstMode = pScrn->modes; + while (pMode) { + newmodes = xrealloc(modes, (num + 1) * sizeof (DGAModeRec)); + if (!newmodes) { + xfree(modes); + return FALSE; + } + modes = newmodes; + currentMode = modes + num; + num++; + (void)memset(currentMode, 1, sizeof(DGAModeRec)); + currentMode->mode = pMode; + currentMode->flags = DGA_PIXMAP_AVAILABLE + | ((pTseng->UseAccel) ? (DGA_FILL_RECT | DGA_BLIT_RECT) : 0); + if (pMode->Flags & V_DBLSCAN) + currentMode->flags |= DGA_DOUBLESCAN; + if(pMode->Flags & V_INTERLACE) + currentMode->flags |= DGA_INTERLACED; + currentMode->byteOrder = pScrn->imageByteOrder; + currentMode->depth = pScrn->depth; + currentMode->bitsPerPixel = pScrn->bitsPerPixel; + currentMode->red_mask = pScrn->mask.red; + currentMode->green_mask = pScrn->mask.green; + currentMode->blue_mask = pScrn->mask.blue; + currentMode->visualClass = (Bpp == 1) ? PseudoColor : TrueColor; + currentMode->viewportWidth = pMode->HDisplay; + currentMode->viewportHeight = pMode->VDisplay; + currentMode->xViewportStep = 1; /* The granularity of x and y pos. */ + currentMode->yViewportStep = 1; + currentMode->viewportFlags = 0 /*DGA_FLIP_RETRACE*/; + currentMode->offset = 0; + currentMode->address = pTseng->FbBase; + currentMode->bytesPerScanline = ((pScrn->displayWidth * Bpp) + 3) & ~3L; + currentMode->pixmapWidth = currentMode->imageWidth = pScrn->displayWidth; + currentMode->pixmapHeight = currentMode->imageHeight = imlines; + currentMode->maxViewportX = currentMode->imageWidth - + currentMode->viewportWidth; + /* this might need to get clamped to some maximum */ + currentMode->maxViewportY = currentMode->imageHeight - + currentMode->viewportHeight; + + pMode = pMode->next; + if(pMode == firstMode) + break; + } + pTseng->DGAnumModes = num; + pTseng->DGAModes = modes; + } + return DGAInit(pScreen, &TsengDGAFuncs, pTseng->DGAModes, pTseng->DGAnumModes); +} + +static Bool +Tseng_OpenFramebuffer( + ScrnInfoPtr pScrn, + char **name, + unsigned char **mem, + int *size, + int *offset, + int *flags +){ + TsengPtr pTseng = TsengPTR(pScrn); + + *name = NULL; /* no special device */ + *mem = (unsigned char*)pTseng->LinFbAddress; + *size = pTseng->FbMapSize; + *offset = 0; /* Always */ + *flags = 0; /* Root permissions OS-dependent */ + + return TRUE; +} + + +static Bool +Tseng_SetMode( + ScrnInfoPtr pScrn, + DGAModePtr pMode +){ + TsengPtr pTseng = TsengPTR(pScrn); + static int OldDisplayWidth[MAXSCREENS]; + int index = pScrn->pScreen->myNum; + Bool ret; + + if(!pMode) { /* restore the original mode */ + /* put the ScreenParameters back */ + pScrn->displayWidth = OldDisplayWidth[index]; + ret = TsengModeInit(xf86Screens[index], pScrn->currentMode); + pTseng->DGAactive = FALSE; + } else { + if(!pTseng->DGAactive) { /* save the old parameters */ + OldDisplayWidth[index] = pScrn->displayWidth; + + pTseng->DGAactive = TRUE; + } + pScrn->displayWidth = pMode->bytesPerScanline / + (pMode->bitsPerPixel >> 3); + + ret = TsengModeInit(xf86Screens[index], pMode->mode); + } + return ret; +} + +static void +Tseng_SetViewport( + ScrnInfoPtr pScrn, + int x, int y, + int flags +){ + TsengPtr pTseng = TsengPTR(pScrn); + vgaHWPtr hwp = VGAHWPTR(pScrn); + + TsengAdjustFrame(pScrn->pScreen->myNum, x, y, flags); + while((hwp->readST01(hwp) & 0x08)); + while(!(hwp->readST01(hwp) & 0x08)); + + pTseng->DGAViewportStatus = 0; /* TsengAdjustFrame loops until finished */ +} + +static int +Tseng_GetViewport( + ScrnInfoPtr pScrn +){ + TsengPtr pTseng = TsengPTR(pScrn); + + return pTseng->DGAViewportStatus; +} + + + +static void +Tseng_Sync( + ScrnInfoPtr pScrn +){ + TsengPtr pTseng = TsengPTR(pScrn); + + if(pTseng->AccelInfoRec) { + (*pTseng->AccelInfoRec->Sync)(pScrn); + } +} + +static void +Tseng_FillRect ( + ScrnInfoPtr pScrn, + int x, int y, int w, int h, + unsigned long color +){ + TsengPtr pTseng = TsengPTR(pScrn); + + if(pTseng->AccelInfoRec) { + (*pTseng->AccelInfoRec->SetupForSolidFill)(pScrn, color, GXcopy, ~0); + (*pTseng->AccelInfoRec->SubsequentSolidFillRect)(pScrn, x, y, w, h); + SET_SYNC_FLAG(pTseng->AccelInfoRec); + } +} + +static void +Tseng_BlitRect( + ScrnInfoPtr pScrn, + int srcx, int srcy, + int w, int h, + int dstx, int dsty +){ + TsengPtr pTseng = TsengPTR(pScrn); + + if(pTseng->AccelInfoRec) { + int xdir = ((srcx < dstx) && (srcy == dsty)) ? -1 : 1; + int ydir = (srcy < dsty) ? -1 : 1; + + (*pTseng->AccelInfoRec->SetupForScreenToScreenCopy)( + pScrn, xdir, ydir, GXcopy, ~0, -1); + (*pTseng->AccelInfoRec->SubsequentScreenToScreenCopy)( + pScrn, srcx, srcy, dstx, dsty, w, h); + SET_SYNC_FLAG(pTseng->AccelInfoRec); + } +} diff --git a/src/tseng_dpms.c b/src/tseng_dpms.c new file mode 100644 index 0000000..c1e2113 --- /dev/null +++ b/src/tseng_dpms.c @@ -0,0 +1,249 @@ + +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/tseng_dpms.c,v 1.10 2001/01/21 21:19:35 tsi Exp $ */ + + + + + + +#include "tseng.h" + +/* + * TsengCrtcDPMSSet -- + * + * Sets VESA Display Power Management Signaling (DPMS) Mode. + * This routine is for the ET4000W32P rev. c and later, which can + * use CRTC indexed register 34 to turn off H/V Sync signals. + * + * '97 Harald Nordgård Hansen + */ +void +TsengCrtcDPMSSet(ScrnInfoPtr pScrn, + int PowerManagementMode, int flags) +{ + unsigned char seq1, crtc34; + int iobase = VGAHWPTR(pScrn)->IOBase; + + xf86EnableAccess(pScrn); + switch (PowerManagementMode) { + case DPMSModeOn: + default: + /* Screen: On; HSync: On, VSync: On */ + seq1 = 0x00; + crtc34 = 0x00; + break; + case DPMSModeStandby: + /* Screen: Off; HSync: Off, VSync: On */ + seq1 = 0x20; + crtc34 = 0x01; + break; + case DPMSModeSuspend: + /* Screen: Off; HSync: On, VSync: Off */ + seq1 = 0x20; + crtc34 = 0x20; + break; + case DPMSModeOff: + /* Screen: Off; HSync: Off, VSync: Off */ + seq1 = 0x20; + crtc34 = 0x21; + break; + } + outb(0x3C4, 0x01); /* Select SEQ1 */ + seq1 |= inb(0x3C5) & ~0x20; + outb(0x3C5, seq1); + outb(iobase + 4, 0x34); /* Select CRTC34 */ + crtc34 |= inb(iobase + 5) & ~0x21; + outb(iobase + 5, crtc34); +} + +/* + * TsengHVSyncDPMSSet -- + * + * Sets VESA Display Power Management Signaling (DPMS) Mode. + * This routine is for Tseng et4000 chips that do not have any + * registers to disable sync output. + * + * The "classic" (standard VGA compatible) method; disabling all syncs, + * causes video memory corruption on Tseng cards, according to "Tseng + * ET4000/W32 family tech note #20": + * + * "Setting CRTC Indexed Register 17 bit 7 = 0 will disable the video + * syncs (=VESA DPMS power down), but will also disable DRAM refresh cycles" + * + * The method used here is derived from the same tech note, which describes + * a method to disable specific sync signals on chips that do not have + * direct support for it: + * + * To get vsync off, program VSYNC_START > VTOTAL + * (approximately). In particular, the formula used is: + * + * VSYNC.ADJ = (VTOT - VSYNC.NORM) + VTOT + 4 + * + * To test for this state, test if VTOT + 1 < VSYNC + * + * + * To get hsync off, program HSYNC_START > HTOTAL + * (approximately). In particular, the following formula is used: + * + * HSYNC.ADJ = (HTOT - HSYNC.NORM) + HTOT + 7 + * + * To test for this state, test if HTOT + 3 < HSYNC + * + * The advantage of these formulas is that the ON state can be restored by + * reversing the formula. The original state need not be stored anywhere... + * + * The trick in the above approach is obviously to put the start of the sync + * _beyond_ the total H or V counter range, which causes the sync to never + * toggle. + */ +void +TsengHVSyncDPMSSet(ScrnInfoPtr pScrn, + int PowerManagementMode, int flags) +{ + unsigned char seq1, tmpb; + unsigned int HSync, VSync, HTot, VTot, tmp; + Bool chgHSync, chgVSync; + int iobase = VGAHWPTR(pScrn)->IOBase; + + /* Code here to read the current values of HSync through VTot: + * HSYNC: + * bits 0..7 : CRTC index 0x04 + * bit 8 : CRTC index 0x3F, bit 4 + */ + outb(iobase + 4, 0x04); + HSync = inb(iobase + 5); + outb(iobase + 4, 0x3F); + HSync += (inb(iobase + 5) & 0x10) << 4; + /* VSYNC: + * bits 0..7 : CRTC index 0x10 + * bits 8..9 : CRTC index 0x07 bits 2 (VSYNC bit 8) and 7 (VSYNC bit 9) + * bit 10 : CRTC index 0x35 bit 3 + */ + outb(iobase + 4, 0x10); + VSync = inb(iobase + 5); + outb(iobase + 4, 0x07); + tmp = inb(iobase + 5); + VSync += ((tmp & 0x04) << 6) + ((tmp & 0x80) << 2); + outb(iobase + 4, 0x35); + VSync += (inb(iobase + 5) & 0x08) << 7; + /* HTOT: + * bits 0..7 : CRTC index 0x00. + * bit 8 : CRTC index 0x3F, bit 0 + */ + outb(iobase + 4, 0x00); + HTot = inb(iobase + 5); + outb(iobase + 4, 0x3F); + HTot += (inb(iobase + 5) & 0x01) << 8; + /* VTOT: + * bits 0..7 : CRTC index 0x06 + * bits 8..9 : CRTC index 0x07 bits 0 (VTOT bit 8) and 5 (VTOT bit 9) + * bit 10 : CRTC index 0x35 bit 1 + */ + outb(iobase + 4, 0x06); + VTot = inb(iobase + 5); + outb(iobase + 4, 0x07); + tmp = inb(iobase + 5); + VTot += ((tmp & 0x01) << 8) + ((tmp & 0x20) << 4); + outb(iobase + 4, 0x35); + VTot += (inb(iobase + 5) & 0x02) << 9; + + /* Don't write these unless we have to. */ + chgHSync = chgVSync = FALSE; + + switch (PowerManagementMode) { + case DPMSModeOn: + default: + /* Screen: On; HSync: On, VSync: On */ + seq1 = 0x00; + if (HSync > HTot + 3) { /* Sync is off now, turn it on. */ + HSync = (HTot - HSync) + HTot + 7; + chgHSync = TRUE; + } + if (VSync > VTot + 1) { /* Sync is off now, turn it on. */ + VSync = (VTot - VSync) + VTot + 4; + chgVSync = TRUE; + } + break; + case DPMSModeStandby: + /* Screen: Off; HSync: Off, VSync: On */ + seq1 = 0x20; + if (HSync <= HTot + 3) { /* Sync is on now, turn it off. */ + HSync = (HTot - HSync) + HTot + 7; + chgHSync = TRUE; + } + if (VSync > VTot + 1) { /* Sync is off now, turn it on. */ + VSync = (VTot - VSync) + VTot + 4; + chgVSync = TRUE; + } + break; + case DPMSModeSuspend: + /* Screen: Off; HSync: On, VSync: Off */ + seq1 = 0x20; + if (HSync > HTot + 3) { /* Sync is off now, turn it on. */ + HSync = (HTot - HSync) + HTot + 7; + chgHSync = TRUE; + } + if (VSync <= VTot + 1) { /* Sync is on now, turn it off. */ + VSync = (VTot - VSync) + VTot + 4; + chgVSync = TRUE; + } + break; + case DPMSModeOff: + /* Screen: Off; HSync: Off, VSync: Off */ + seq1 = 0x20; + if (HSync <= HTot + 3) { /* Sync is on now, turn it off. */ + HSync = (HTot - HSync) + HTot + 7; + chgHSync = TRUE; + } + if (VSync <= VTot + 1) { /* Sync is on now, turn it off. */ + VSync = (VTot - VSync) + VTot + 4; + chgVSync = TRUE; + } + break; + } + + /* If the new hsync or vsync overflows, don't change anything. */ + if (HSync >= 1 << 9 || VSync >= 1 << 11) { + ErrorF("tseng: warning: Cannot go into DPMS from this resolution.\n"); + chgVSync = chgHSync = FALSE; + } + /* The code to turn on and off video output is equal for all. */ + if (chgHSync || chgVSync) { + outb(0x3C4, 0x01); /* Select SEQ1 */ + seq1 |= inb(0x3C5) & ~0x20; + outb(0x3C5, seq1); + } + /* Then the code to write VSync and HSync to the card. + * HSYNC: + * bits 0..7 : CRTC index 0x04 + * bit 8 : CRTC index 0x3F, bit 4 + */ + if (chgHSync) { + outb(iobase + 4, 0x04); + tmpb = HSync & 0xFF; + outb(iobase + 5, tmpb); + outb(iobase + 4, 0x3F); + tmpb = (HSync & 0x100) >> 4; + tmpb |= inb(iobase + 5) & ~0x10; + outb(iobase + 5, tmpb); + } + /* VSYNC: + * bits 0..7 : CRTC index 0x10 + * bits 8..9 : CRTC index 0x07 bits 2 (VSYNC bit 8) and 7 (VSYNC bit 9) + * bit 10 : CRTC index 0x35 bit 3 + */ + if (chgVSync) { + outb(iobase + 4, 0x10); + tmpb = VSync & 0xFF; + outb(iobase + 5, tmpb); + outb(iobase + 4, 0x07); + tmpb = (VSync & 0x100) >> 6; + tmpb |= (VSync & 0x200) >> 2; + tmpb |= inb(iobase + 5) & ~0x84; + outb(iobase + 5, tmpb); + outb(iobase + 4, 0x35); + tmpb = (VSync & 0x400) >> 7; + tmpb |= inb(iobase + 5) & ~0x08; + outb(iobase + 5, tmpb); + } +} diff --git a/src/tseng_driver.c b/src/tseng_driver.c new file mode 100644 index 0000000..e59ff9e --- /dev/null +++ b/src/tseng_driver.c @@ -0,0 +1,3292 @@ +/* + * $XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/tseng_driver.c,v 1.91 2002/07/24 01:47:34 tsi Exp $ + * + * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Thomas Roell not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Thomas Roell makes no representations + * about the suitability of this software for any purpose. It is provided + * "as is" without express or implied warranty. + * + * THOMAS ROELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THOMAS ROELL BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * Author: Thomas Roell, roell@informatik.tu-muenchen.de + * ET6000 and ET4000W32 16/24/32 bpp and acceleration support by Koen Gadeyne + * + * Large parts rewritten for XFree86 4.0 by Koen Gadeyne. + */ +/* $XConsortium: et4_driver.c /main/27 1996/10/28 04:48:15 kaleb $ */ + + + + + +/*** Generic includes ***/ + +#include "tseng.h" /* this includes most of the generic ones as well */ +#include "tseng_acl.h" + +/* All drivers initialising the SW cursor need this */ +#include "mipointer.h" + +/* All drivers implementing backing store need this */ +#include "mibstore.h" + +#include "fb.h" + +#include "xf86RAC.h" +#include "xf86Resources.h" +#include "xf86int10.h" + +#ifdef XvExtension +#include "xf86xv.h" +#include "Xv.h" +#endif + +/*** Chip-specific includes ***/ + +/* #include "tseng_acl.h" */ + +/* + * Forward definitions for the functions that make up the driver. + */ + +/* Mandatory functions */ +static const OptionInfoRec * TsengAvailableOptions(int chipid, int busid); +static void TsengIdentify(int flags); +static Bool TsengProbe(DriverPtr drv, int flags); +static Bool TsengPreInit(ScrnInfoPtr pScrn, int flags); +static Bool TsengScreenInit(int Index, ScreenPtr pScreen, int argc, + char **argv); +static Bool TsengEnterVT(int scrnIndex, int flags); +static void TsengLeaveVT(int scrnIndex, int flags); +static Bool TsengCloseScreen(int scrnIndex, ScreenPtr pScreen); +static Bool TsengSaveScreen(ScreenPtr pScreen, int mode); + +/* Required if the driver supports mode switching */ +static Bool TsengSwitchMode(int scrnIndex, DisplayModePtr mode, int flags); + +/* Optional functions */ +static void TsengFreeScreen(int scrnIndex, int flags); +static ModeStatus TsengValidMode(int scrnIndex, DisplayModePtr mode, + Bool verbose, int flags); + +/* If driver-specific config file entries are needed, this must be defined */ +/*static Bool TsengParseConfig(ParseInfoPtr raw); */ + +/* Internally used functions (some are defined in tseng.h) */ +static Bool TsengMapMem(ScrnInfoPtr pScrn); +static Bool TsengUnmapMem(ScrnInfoPtr pScrn); +static void TsengSave(ScrnInfoPtr pScrn); +static void TsengRestore(ScrnInfoPtr pScrn, vgaRegPtr vgaReg, TsengRegPtr tsengReg, int flags); +static void TsengUnlock(void); +static void TsengLock(void); + +static Bool ET4000DetailedProbe(t_tseng_type * chiptype, t_w32_revid * rev); + +/* + * This is intentionally screen-independent. It indicates the binding + * choice made in the first PreInit. + */ +static int pix24bpp = 0; + +#define VERSION 4000 +#define TSENG_NAME "TSENG" +#define TSENG_DRIVER_NAME "tseng" +#define TSENG_MAJOR_VERSION 1 +#define TSENG_MINOR_VERSION 0 +#define TSENG_PATCHLEVEL 0 + +/* CRTC timing limits */ +#define Tseng_HMAX (4096-8) +#define Tseng_VMAX (2048-1) + +/* + * This contains the functions needed by the server after loading the + * driver module. It must be supplied, and gets added the driver list by + * the Module Setup funtion in the dynamic case. In the static case a + * reference to this is compiled in, and this requires that the name of + * this DriverRec be an upper-case version of the driver name. + */ + +DriverRec TSENG = +{ + VERSION, + TSENG_DRIVER_NAME, + TsengIdentify, + TsengProbe, + TsengAvailableOptions, + NULL, + 0 +}; + +/* sub-revisions are now dealt with in the ChipRev variable */ +static SymTabRec TsengChipsets[] = +{ + {TYPE_ET4000, "ET4000"}, + {TYPE_ET4000W32, "ET4000W32"}, + {TYPE_ET4000W32I, "ET4000W32i"}, + {TYPE_ET4000W32P, "ET4000W32p"}, + {TYPE_ET6000, "ET6000"}, + {TYPE_ET6100, "ET6100"}, + {TYPE_TSENG, ""}, + {-1, NULL} +}; + +/* Convert PCI ID to chipset name */ +static PciChipsets TsengPciChipsets[] = +{ + {TYPE_ET4000W32P, PCI_CHIP_ET4000_W32P_A, RES_SHARED_VGA}, + {TYPE_ET4000W32P, PCI_CHIP_ET4000_W32P_B, RES_SHARED_VGA}, + {TYPE_ET4000W32P, PCI_CHIP_ET4000_W32P_C, RES_SHARED_VGA}, + {TYPE_ET4000W32P, PCI_CHIP_ET4000_W32P_D, RES_SHARED_VGA}, + {TYPE_ET6000, PCI_CHIP_ET6000, RES_SHARED_VGA}, + {-1, -1, RES_UNDEFINED} +}; + +static IsaChipsets TsengIsaChipsets[] = +{ + {TYPE_ET4000, RES_EXCLUSIVE_VGA}, + {TYPE_ET4000W32, RES_EXCLUSIVE_VGA}, + {TYPE_ET4000W32I, RES_EXCLUSIVE_VGA}, + {TYPE_TSENG, RES_EXCLUSIVE_VGA}, + {-1, RES_UNDEFINED} +}; + +typedef enum { + OPTION_HIBIT_HIGH, + OPTION_HIBIT_LOW, + OPTION_SW_CURSOR, + OPTION_HW_CURSOR, + OPTION_PCI_BURST, + OPTION_SLOW_DRAM, + OPTION_MED_DRAM, + OPTION_FAST_DRAM, + OPTION_W32_INTERLEAVE, + OPTION_NOACCEL, + OPTION_NOCLOCKCHIP, + OPTION_LINEAR, + OPTION_SHOWCACHE, + OPTION_LEGEND, + OPTION_PCI_RETRY, + OPTION_SET_MCLK +} TsengOpts; + +static const OptionInfoRec TsengOptions[] = +{ + {OPTION_HIBIT_HIGH, "hibit_high", OPTV_BOOLEAN, + {0}, FALSE}, + {OPTION_HIBIT_LOW, "hibit_low", OPTV_BOOLEAN, + {0}, FALSE}, + {OPTION_SW_CURSOR, "SWcursor", OPTV_BOOLEAN, + {0}, FALSE}, + {OPTION_HW_CURSOR, "HWcursor", OPTV_BOOLEAN, + {0}, FALSE}, + {OPTION_PCI_BURST, "pci_burst", OPTV_BOOLEAN, + {0}, FALSE}, + {OPTION_SLOW_DRAM, "slow_dram", OPTV_BOOLEAN, + {0}, FALSE}, + {OPTION_MED_DRAM, "med_dram", OPTV_BOOLEAN, + {0}, FALSE}, + {OPTION_FAST_DRAM, "fast_dram", OPTV_BOOLEAN, + {0}, FALSE}, + {OPTION_W32_INTERLEAVE, "w32_interleave", OPTV_BOOLEAN, + {0}, FALSE}, + {OPTION_NOACCEL, "NoAccel", OPTV_BOOLEAN, + {0}, FALSE}, + {OPTION_NOCLOCKCHIP, "NoClockchip", OPTV_BOOLEAN, + {0}, FALSE}, + {OPTION_LINEAR, "Linear", OPTV_BOOLEAN, + {0}, FALSE}, + {OPTION_SHOWCACHE, "ShowCache", OPTV_BOOLEAN, + {0}, FALSE}, + {OPTION_LEGEND, "Legend", OPTV_BOOLEAN, + {0}, FALSE}, + {OPTION_PCI_RETRY, "PciRetry", OPTV_BOOLEAN, + {0}, FALSE}, + {OPTION_SET_MCLK, "SetMClk", OPTV_FREQ, + {0}, FALSE}, + {-1, NULL, OPTV_NONE, + {0}, FALSE} +}; + +static const char *int10Symbols[] = { + "xf86FreeInt10", + "xf86InitInt10", + NULL +}; + +static const char *vgaHWSymbols[] = { + "vgaHWFreeHWRec", + "vgaHWGetHWRec", + "vgaHWGetIOBase", + "vgaHWGetIndex", + "vgaHWHandleColormaps", + "vgaHWInit", + "vgaHWLock", + "vgaHWMapMem", + "vgaHWProtect", + "vgaHWRestore", + "vgaHWSave", + "vgaHWSaveScreen", + "vgaHWUnlock", + "vgaHWUnmapMem", + NULL +}; + +static const char* miscfbSymbols[] = { + "xf1bppScreenInit", + "xf4bppScreenInit", + NULL +}; + +static const char* fbSymbols[] = { + "fbPictureInit", + "fbScreenInit", + NULL +}; + +static const char *ramdacSymbols[] = { + "xf86CreateCursorInfoRec", + "xf86DestroyCursorInfoRec", + "xf86InitCursor", + NULL +}; + +static const char *xaaSymbols[] = { + "XAACreateInfoRec", + "XAADestroyInfoRec", + "XAAInit", + NULL +}; + +#ifdef XFree86LOADER + +static MODULESETUPPROTO(tsengSetup); + +static XF86ModuleVersionInfo tsengVersRec = +{ + "tseng", + MODULEVENDORSTRING, + MODINFOSTRING1, + MODINFOSTRING2, + XF86_VERSION_CURRENT, + TSENG_MAJOR_VERSION, TSENG_MINOR_VERSION, TSENG_PATCHLEVEL, + ABI_CLASS_VIDEODRV, /* This is a video driver */ + ABI_VIDEODRV_VERSION, + MOD_CLASS_VIDEODRV, + {0, 0, 0, 0} +}; + +/* + * This is the module init data for XFree86 modules. + * + * Its name has to be the driver name followed by ModuleData. + */ +XF86ModuleData tsengModuleData = { &tsengVersRec, tsengSetup, NULL }; + +static pointer +tsengSetup(pointer module, pointer opts, int *errmaj, int *errmin) +{ + static Bool setupDone = FALSE; + + if (!setupDone) { + setupDone = TRUE; + xf86AddDriver(&TSENG, module, 0); + + /* + * Modules that this driver always requires can be loaded here + * by calling LoadSubModule(). + */ + /* + * Tell the loader about symbols from other modules that this module + * might refer to. + */ + LoaderRefSymLists(vgaHWSymbols, miscfbSymbols, fbSymbols, xaaSymbols, + int10Symbols, ramdacSymbols, NULL); + + /* + * The return value must be non-NULL on success even though there + * is no TearDownProc. + */ + return (pointer) 1; + } else { + if (errmaj) + *errmaj = LDR_ONCEONLY; + return NULL; + } +} + +#endif /* XFree86LOADER */ + +static Bool +TsengGetRec(ScrnInfoPtr pScrn) +{ + PDEBUG(" TsengGetRec\n"); + /* + * Allocate an TsengRec, and hook it into pScrn->driverPrivate. + * pScrn->driverPrivate is initialised to NULL, so we can check if + * the allocation has already been done. + */ + if (pScrn->driverPrivate != NULL) + return TRUE; + + pScrn->driverPrivate = xnfcalloc(sizeof(TsengRec), 1); + /* Initialise it here when needed (or possible) */ + + return TRUE; +} + +static void +TsengFreeRec(ScrnInfoPtr pScrn) +{ + PDEBUG(" TsengFreeRec\n"); + if (pScrn->driverPrivate == NULL) + return; + xfree(pScrn->driverPrivate); + pScrn->driverPrivate = NULL; +} + +static t_tseng_type +TsengPCI2Type(ScrnInfoPtr pScrn, int ChipID) +{ + TsengPtr pTseng = TsengPTR(pScrn); + + switch (ChipID) { + case PCI_CHIP_ET4000_W32P_A: + pTseng->ChipType = TYPE_ET4000W32P; + pTseng->ChipRev = W32REVID_A; + break; + case PCI_CHIP_ET4000_W32P_B: + pTseng->ChipType = TYPE_ET4000W32P; + pTseng->ChipRev = W32REVID_B; + break; + case PCI_CHIP_ET4000_W32P_C: + pTseng->ChipType = TYPE_ET4000W32P; + pTseng->ChipRev = W32REVID_C; + break; + case PCI_CHIP_ET4000_W32P_D: + pTseng->ChipType = TYPE_ET4000W32P; + pTseng->ChipRev = W32REVID_D; + break; + case PCI_CHIP_ET6000: + pTseng->ChipType = TYPE_ET6000; + pTseng->ChipRev = pTseng->PciInfo->chipRev; /* ET6000 != ET6100 */ + if (pTseng->ChipRev >= ET6100REVID) + pTseng->ChipType = TYPE_ET6100; + break; + default: + xf86Msg(X_ERROR, "%s: Unknown Tseng PCI ID: %X\n", TSENG_NAME, ChipID); + return FALSE; + } + return TRUE; +} + +static const OptionInfoRec * +TsengAvailableOptions(int chipid, int busid) +{ + return TsengOptions; +} + +static void +TsengIdentify(int flags) +{ + PDEBUG(" TsengIdentify\n"); + xf86PrintChipsets(TSENG_NAME, "driver for Tseng Labs chipsets", + TsengChipsets); +} + +static void +TsengAssignFPtr(ScrnInfoPtr pScrn) +{ + pScrn->driverVersion = VERSION; + pScrn->driverName = TSENG_DRIVER_NAME; + pScrn->name = TSENG_NAME; + pScrn->Probe = TsengProbe; + pScrn->PreInit = TsengPreInit; + pScrn->ScreenInit = TsengScreenInit; + pScrn->SwitchMode = TsengSwitchMode; + pScrn->AdjustFrame = TsengAdjustFrame; + pScrn->EnterVT = TsengEnterVT; + pScrn->LeaveVT = TsengLeaveVT; + pScrn->FreeScreen = TsengFreeScreen; + pScrn->ValidMode = TsengValidMode; +} + +/* unlock ET4000 using KEY register */ +static void +TsengUnlock(void) +{ + unsigned char temp; + int iobase = VGAHW_GET_IOBASE(); + + PDEBUG(" TsengUnlock\n"); + outb(0x3BF, 0x03); + outb(iobase + 8, 0xA0); + outb(iobase + 4, 0x11); + temp = inb(iobase + 5); + outb(iobase + 5, temp & 0x7F); +} + +/* lock ET4000 using KEY register. FIXME: should restore old lock status instead */ +static void +TsengLock(void) +{ + unsigned char temp; + int iobase = VGAHW_GET_IOBASE(); + + PDEBUG(" TsengLock\n"); + outb(iobase + 4, 0x11); + temp = inb(iobase + 5); + outb(iobase + 5, temp | 0x80); + outb(iobase + 8, 0x00); + outb(0x3D8, 0x29); + outb(0x3BF, 0x01); +} + +/* + * ET4000AutoDetect -- Old-style autodetection code (by register probing) + * + * This code is only called when the chipset is not given beforehand, + * and if the PCI code hasn't detected one previously. + */ +#if 1 +static Bool +ET4000MinimalProbe(void) +{ + unsigned char temp, origVal, newVal; + int iobase; + + PDEBUG(" ET4000MinimalProbe\n"); + /* + * Note, the vgaHW module cannot be used here, but there are + * some macros in vgaHW.h that can be used. + */ + iobase = VGAHW_GET_IOBASE(); + + /* + * Check first that there is a ATC[16] register and then look at + * CRTC[33]. If both are R/W correctly it's a ET4000 ! + */ + temp = inb(iobase + 0x0A); + TsengUnlock(); /* only ATC 0x16 is protected by KEY */ + outb(0x3C0, 0x16 | 0x20); + origVal = inb(0x3C1); + outb(0x3C0, origVal ^ 0x10); + outb(0x3C0, 0x16 | 0x20); + newVal = inb(0x3C1); + outb(0x3C0, origVal); +/* TsengLock(); FIXME: RESTORE OLD CONTENTS INSTEAD ! */ + if (newVal != (origVal ^ 0x10)) { + return (FALSE); + } + outb(iobase + 0x04, 0x33); + origVal = inb(iobase + 0x05); + outb(iobase + 0x05, origVal ^ 0x0F); + newVal = inb(iobase + 0x05); + outb(iobase + 0x05, origVal); + if (newVal != (origVal ^ 0x0F)) { + return (FALSE); + } + return TRUE; +} +#endif + +static int +TsengFindIsaDevice(GDevPtr dev) +{ + /* XXX Need to implement this */ + if (ET4000MinimalProbe()) + return TYPE_TSENG; + + return -1; +} + +static Bool +TsengProbe(DriverPtr drv, int flags) +{ + int i; + GDevPtr *devSections; + int numDevSections; + int numUsed; + int *usedChips = NULL; + Bool foundScreen = FALSE; + + + PDEBUG(" TsengProbe\n"); + /* + * The aim here is to find all cards that this driver can handle, + * and for the ones not already claimed by another driver, claim the + * slot, and allocate a ScrnInfoRec. + * + * This should be a minimal probe, and it should under no circumstances + * change the state of the hardware. Because a device is found, don't + * assume that it will be used. Don't do any initialisations other than + * the required ScrnInfoRec initialisations. Don't allocate any new + * data structures. + */ + + /* + * Find the config file Device sections that match this + * driver, and return if there are none. + */ + if ((numDevSections = xf86MatchDevice(TSENG_DRIVER_NAME, + &devSections)) <= 0) { + return FALSE; + } + + /* XXX maybe this can go some time soon */ + /* + * for the Tseng server, there can only be one matching + * device section. So issue a warning if more than one show up. + * Multiple Tseng cards in the same machine are not possible. + */ + /* + * If this is a PCI card, "probing" just amounts to checking the PCI + * data that the server has already collected. If there is none, + * check for non-PCI boards. + * + * The provided xf86MatchPciInstances() helper takes care of + * the details. + */ + numUsed = 0; + if (xf86GetPciVideoInfo() != NULL) { + numUsed = xf86MatchPciInstances(TSENG_NAME, PCI_VENDOR_TSENG, + TsengChipsets, TsengPciChipsets, + devSections,numDevSections, drv, + &usedChips); + if (numUsed > 0) { + if (flags & PROBE_DETECT) + foundScreen = TRUE; + else for (i = 0; i < numUsed; i++) { + /* Allocate a ScrnInfoRec */ + ScrnInfoPtr pScrn = NULL; + if ((pScrn = xf86ConfigPciEntity(pScrn,0,usedChips[i], + TsengPciChipsets,NULL, + NULL,NULL,NULL,NULL))) { + TsengAssignFPtr(pScrn); + foundScreen = TRUE; + } + } + xfree(usedChips); + } + } + + /* Check for non-PCI cards */ + numUsed = xf86MatchIsaInstances(TSENG_NAME, TsengChipsets, + TsengIsaChipsets,drv, TsengFindIsaDevice, devSections, + numDevSections, &usedChips); + if (numUsed > 0) { + if (flags & PROBE_DETECT) + foundScreen = TRUE; + else for (i = 0; i < numUsed; i++) { + ScrnInfoPtr pScrn = NULL; + if ((pScrn = xf86ConfigIsaEntity(pScrn,0,usedChips[i], + TsengIsaChipsets,NULL, + NULL,NULL,NULL,NULL))) { + TsengAssignFPtr(pScrn); + foundScreen = TRUE; + } + } + xfree(usedChips); + } + xfree(devSections); + return foundScreen; +} + +/* The PCI part of TsengPreInit() */ +static Bool +TsengPreInitPCI(ScrnInfoPtr pScrn) +{ + MessageType from; + TsengPtr pTseng = TsengPTR(pScrn); + + PDEBUG(" TsengPreInitPCI\n"); + /* + * * Set the ChipType and ChipRev, allowing config file entries to + * * override. + */ + if (pTseng->pEnt->device->chipset && *pTseng->pEnt->device->chipset) { + /* chipset given as a string in the config file */ + pScrn->chipset = pTseng->pEnt->device->chipset; + pTseng->ChipType = xf86StringToToken(TsengChipsets, pScrn->chipset); + /* FIXME: still need to probe for W32p revision here */ + from = X_CONFIG; + } else if (pTseng->pEnt->device->chipID >= 0) { + /* chipset given as a PCI ID in the config file */ + if (!TsengPCI2Type(pScrn, pTseng->pEnt->device->chipID)) + return FALSE; + pScrn->chipset = (char *)xf86TokenToString(TsengChipsets, pTseng->ChipType); + from = X_CONFIG; + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ChipID override: 0x%04X\n", + pTseng->ChipType); + } else { + /* probe for chipset type */ + from = X_PROBED; + if (!TsengPCI2Type(pScrn, pTseng->PciInfo->chipType)) + return FALSE; + pScrn->chipset = (char *)xf86TokenToString(TsengChipsets, pTseng->ChipType); + } + + if (pTseng->pEnt->device->chipRev >= 0) { + pTseng->ChipRev = pTseng->pEnt->device->chipRev; + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ChipRev override: %d\n", + pTseng->ChipRev); + if ((pTseng->ChipType == TYPE_ET6000) && (pTseng->ChipRev >= ET6100REVID)) + pTseng->ChipType = TYPE_ET6100; + } else { + t_tseng_type dum_chiptype; + t_w32_revid rev; + if (Is_ET6K) { + pTseng->ChipRev = pTseng->PciInfo->chipRev; + } else { + /* + * on W32p cards, the PCI ChipRev field is always 0 (it is not + * implemented). We will use the ChipRev field to distinguish + * between revisions A through D, so we set it up with the + * standard register-probing "Detailed Probe" instead. + */ + ET4000DetailedProbe(&dum_chiptype, &rev); + pTseng->ChipRev = rev; + } + } + + if (Is_ET6K) { + xf86DrvMsg(pScrn->scrnIndex, from, "Chipset: \"%s\"\n", pScrn->chipset); + } else { + char ch_rev; + switch (pTseng->ChipRev) { + case W32REVID_A: + ch_rev = 'A'; + break; + case W32REVID_B: + ch_rev = 'B'; + break; + case W32REVID_C: + ch_rev = 'C'; + break; + case W32REVID_D: + ch_rev = 'D'; + break; + default: + ch_rev = 'X'; + } + xf86DrvMsg(pScrn->scrnIndex, from, "Chipset: \"%s\" (rev %c)\n", + pScrn->chipset, ch_rev); + } + + pTseng->PciTag = pciTag(pTseng->PciInfo->bus, pTseng->PciInfo->device, + pTseng->PciInfo->func); + + /* only the ET6000 implements a PCI IO address */ + if (Is_ET6K) { + if (pTseng->pEnt->device->IOBase != 0) { + pTseng->IOAddress = pTseng->pEnt->device->IOBase; + from = X_CONFIG; + } else { + if ((pTseng->PciInfo->ioBase[1]) != 0) { + pTseng->IOAddress = pTseng->PciInfo->ioBase[1]; + from = X_PROBED; + } else { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "No valid PCI I/O address in PCI config space\n"); + return FALSE; + } + } + xf86DrvMsg(pScrn->scrnIndex, from, "PCI I/O registers at 0x%lX\n", + (unsigned long)pTseng->IOAddress); + } + + pTseng->LinFbAddressMask = 0xFF000000; + + return TRUE; +} + +/* + * This is a more detailed probe. We already know it's a Tseng chip from + * TsengPreInit() and ET4000MinimalProbe(). + */ + +static Bool +ET4000DetailedProbe(t_tseng_type * chiptype, t_w32_revid * rev) +{ + unsigned char temp, origVal, newVal; + + PDEBUG(" ET4000DetailedProbe\n"); + + /* + * We know it's an ET4000, now check for an ET4000/W32. + * Test for writability of 0x3cb. + */ + + origVal = inb(0x3cb); + outb(0x3cb, 0x33); /* Arbitrary value */ + newVal = inb(0x3cb); + outb(0x3cb, origVal); + if (newVal != 0x33) { /* not a W32; so it's a standard ET4000 */ + *chiptype = TYPE_ET4000; + *rev = TSENGNOREV; + return TRUE; + } + /* We have an ET4000/W32. Now determine the type. */ + outb(0x217a, 0xec); + temp = inb(0x217b) >> 4; + switch (temp) { + case 0: /* ET4000/W32 */ + *chiptype = TYPE_ET4000W32; + break; + case 1: /* ET4000/W32i */ + *chiptype = TYPE_ET4000W32I; + *rev = W32REVID_A; + break; + case 3: /* ET4000/W32i rev b */ + *chiptype = TYPE_ET4000W32I; + *rev = W32REVID_B; + break; + case 11: /* ET4000/W32i rev c */ + *chiptype = TYPE_ET4000W32I; + *rev = W32REVID_C; + break; + case 2: /* ET4000/W32p rev a */ + *chiptype = TYPE_ET4000W32P; + *rev = W32REVID_A; + break; + case 5: /* ET4000/W32p rev b */ + *chiptype = TYPE_ET4000W32P; + *rev = W32REVID_B; + break; + case 6: /* ET4000/W32p rev d */ + *chiptype = TYPE_ET4000W32P; + *rev = W32REVID_D; + break; + case 7: /* ET4000/W32p rev c */ + *chiptype = TYPE_ET4000W32P; + *rev = W32REVID_C; + break; + default: + return (FALSE); + } + + return (TRUE); +} + +/* + * TsengFindBusType -- + * determine bus interface type + * (also determines Lin Mem address mask, because that depends on bustype) + * + * We don't need to bother with PCI busses here: TsengPreInitPCI() took care + * of that. This code isn't called if it's a PCI bus anyway. + */ + +static void +TsengFindNonPciBusType(ScrnInfoPtr pScrn) +{ + unsigned char bus; + TsengPtr pTseng = TsengPTR(pScrn); + + PDEBUG(" TsengFindNonPciBusType\n"); + pTseng->Bustype = T_BUS_ISA; + pTseng->Linmem_1meg = FALSE; + pTseng->LinFbAddressMask = 0; + + switch (pTseng->ChipType) { + case TYPE_ET4000: + break; + case TYPE_ET4000W32: + case TYPE_ET4000W32I: + /* + * Notation: SMx = bit x of Segment Map Comparator (CRTC index 0x30) + * + * We assume the driver code disables the image port (which it does) + * + * ISA: [ A23==SEGE, A22, A21, A20 ] == [ SM1, SM0, 0, 0 ] + * MCA: [ A24, A23, A22, A21, A20 ] == [ SM2, SM1, SM0, 0, 0 ] + * VLB: [ /A26, /A25, /A24, A23, A22, A21, A20 ] == ("/" means inverted!) + * [ SM4, SM3, SM2, SM1, SM0, 0 , 0 ] + */ + outb(0x217A, 0xEF); + bus = inb(0x217B) & 0x60; /* Determine bus type */ + switch (bus) { + case 0x40: + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected W32/W32i bus type: MCA\n"); + pTseng->Bustype = T_BUS_MCA; + pTseng->LinFbAddressMask = 0x01C00000; /* MADE24, A23 and A22 are decoded */ + break; + case 0x60: + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected W32/W32i bus type: Local Bus\n"); + pTseng->Bustype = T_BUS_VLB; + pTseng->LinFbAddressMask = 0x07C00000; /* A26..A22 are decoded */ + break; + case 0x00: + case 0x20: + default: + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected W32/W32i bus type (0x%02X): ISA\n", bus); + pTseng->Bustype = T_BUS_ISA; + pTseng->LinFbAddressMask = 0x00C00000; /* SEGE and A22 are decoded */ + break; + } + break; + case TYPE_ET4000W32P: + outb(0x217A, 0xEF); + bus = inb(0x217B) >> 3; /* Determine bus type */ + switch (bus) { + case 0x1C: /* PCI case already handled in TsengPreInitPCI() */ + pTseng->Bustype = T_BUS_VLB; + pTseng->LinFbAddressMask = 0x3FC00000; /* A29..A22 */ + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected W32p bus type: Local Buffered Bus\n"); + pTseng->Linmem_1meg = TRUE; /* IMA bus support allows for only 1M linear memory */ + break; + case 0x13: + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected W32p bus type: Local Bus (option 1a)\n"); + pTseng->Bustype = T_BUS_VLB; + if (pTseng->ChipRev == W32REVID_A) + pTseng->LinFbAddressMask = 0x07C00000; + else + pTseng->LinFbAddressMask = 0x1FC00000; /* SEGI,A27..A22 */ + break; + case 0x11: + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected W32p bus type: Local Bus (option 1b)\n"); + pTseng->Bustype = T_BUS_VLB; + pTseng->LinFbAddressMask = 0x00C00000; /* SEGI,A22 */ + pTseng->Linmem_1meg = TRUE; /* IMA bus support allows for only 1M linear memory */ + break; + case 0x08: + case 0x0B: + default: + xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "Detected W32p bus type: Local Bus (option 2)\n"); + pTseng->Bustype = T_BUS_VLB; + pTseng->LinFbAddressMask = 0x3FC00000; /* A29..A22 */ + break; + } + if (Is_W32p_cd && (pTseng->LinFbAddressMask = 0x3FC00000)) + pTseng->LinFbAddressMask |= 0xC0000000; /* A31,A30 decoded from PCI config space */ + break; + case TYPE_ET6000: + case TYPE_ET6100: + pTseng->Bustype = T_BUS_PCI; + pTseng->LinFbAddressMask = 0xFF000000; + break; + case TYPE_TSENG: /* generic */ + break; + } +} + +/* The TsengPreInit() part for non-PCI busses */ +static Bool +TsengPreInitNoPCI(ScrnInfoPtr pScrn) +{ + MessageType from; + t_w32_revid rev = TSENGNOREV; + TsengPtr pTseng = TsengPTR(pScrn); + + PDEBUG(" TsengPreInitNoPCI\n"); + /* + * Set the ChipType and ChipRev, allowing config file entries to + * override. + */ + if (pTseng->pEnt->device->chipset && *pTseng->pEnt->device->chipset) { + /* chipset given as a string in the config file */ + pScrn->chipset = pTseng->pEnt->device->chipset; + pTseng->ChipType = xf86StringToToken(TsengChipsets, pScrn->chipset); + from = X_CONFIG; + } else if (pTseng->pEnt->device->chipID > 0) { + /* chipset given as a PCI ID in the config file */ + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "ChipID override only possible for PCI cards\n"); + return FALSE; + } else { + from = X_PROBED; + if (!ET4000DetailedProbe(&(pTseng->ChipType), &rev)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Unknown Tseng chip detected. Try chipset override.\n"); + return FALSE; + } + pScrn->chipset = (char *)xf86TokenToString(TsengChipsets, pTseng->ChipType); + } + if (pTseng->pEnt->device->chipRev >= 0) { + pTseng->ChipRev = pTseng->pEnt->device->chipRev; + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "ChipRev override: %d\n", + pTseng->ChipRev); + } else { + pTseng->ChipRev = rev; + } + + xf86DrvMsg(pScrn->scrnIndex, from, "Chipset: \"%s\"\n", pScrn->chipset); + + TsengFindNonPciBusType(pScrn); + + return TRUE; +} + +/* + * The 8*32kb ET6000 MDRAM granularity causes the more general probe to + * detect too much memory in some configurations, because that code has a + * 8-bank (=256k) granularity. E.g. it fails to recognize 2.25 MB of memory + * (detects 2.5 instead). This function goes to check if the RAM is actually + * there. MDRAM comes in multiples of 4 banks (16, 24, 32, 36, 40, 64, 72, + * 80, ... 32kb-banks), so checking each 64k block should be enough granularity. + * + * No more than the amount of refreshed RAM is checked. Non-refreshed RAM + * won't work anyway. + * + * The same code could be used on other Tseng chips, or even on ANY + * VGA board, but probably only in case of trouble. + * + * FIXME: this should be done using linear memory + */ +#define VIDMEM ((volatile CARD32*)check_vgabase) +#define SEGSIZE (64) /* kb */ + +#define ET6K_SETSEG(seg) \ + outb(0x3CB, ((seg) & 0x30) | ((seg) >> 4)); \ + outb(0x3CD, ((seg) & 0x0f) | ((seg) << 4)); + +static int +et6000_check_videoram(ScrnInfoPtr pScrn, int ram) +{ + unsigned char oldSegSel1, oldSegSel2, oldGR5, oldGR6, oldSEQ2, oldSEQ4; + int segment, i; + int real_ram = 0; + pointer check_vgabase; + Bool fooled = FALSE; + int save_vidmem; + + PDEBUG(" et6000_check_videoram\n"); + if (ram > 4096) { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "Detected more than 4096 kb of video RAM. Clipped to 4096kb\n"); + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + " (Tseng VGA chips can only use 4096kb).\n"); + ram = 4096; + } + if (!vgaHWMapMem(pScrn)) { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "Could not map VGA memory to check for video memory.\n"); + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + " Detected amount may be wrong.\n"); + return ram; + } + check_vgabase = (VGAHWPTR(pScrn)->Base); + + /* + * We need to set the VGA controller in VGA graphics mode, or else we won't + * be able to access the full 4MB memory range. First, we save the + * registers we modify, of course. + */ + + oldSegSel1 = inb(0x3CD); + oldSegSel2 = inb(0x3CB); + outb(0x3CE, 5); + oldGR5 = inb(0x3CF); + outb(0x3CE, 6); + oldGR6 = inb(0x3CF); + outb(0x3C4, 2); + oldSEQ2 = inb(0x3C5); + outb(0x3C4, 4); + oldSEQ4 = inb(0x3C5); + + /* set graphics mode */ + outb(0x3CE, 6); + outb(0x3CF, 5); + outb(0x3CE, 5); + outb(0x3CF, 0x40); + outb(0x3C4, 2); + outb(0x3C5, 0x0f); + outb(0x3C4, 4); + outb(0x3C5, 0x0e); + + /* + * count down from presumed amount of memory in SEGSIZE steps, and + * look at each segment for real RAM. + * + * To select a segment, we cannot use ET4000W32SetReadWrite(), since + * that requires the ScreenPtr, which we don't have here. + */ + + for (segment = (ram / SEGSIZE) - 1; segment >= 0; segment--) { + /* select the segment */ + ET6K_SETSEG(segment); + + /* save contents of memory probing location */ + save_vidmem = *(VIDMEM); + + /* test with pattern */ + *VIDMEM = 0xAAAA5555; + if (*VIDMEM != 0xAAAA5555) { + *VIDMEM = save_vidmem; + continue; + } + /* test with inverted pattern */ + *VIDMEM = 0x5555AAAA; + if (*VIDMEM != 0x5555AAAA) { + *VIDMEM = save_vidmem; + continue; + } + /* + * If we get here, the memory seems to be writable/readable + * Now check if we aren't fooled by address wrapping (mirroring) + */ + fooled = FALSE; + for (i = segment - 1; i >= 0; i--) { + /* select the segment */ + ET6K_SETSEG(i); + outb(0x3CB, (i & 0x30) | (i >> 4)); + outb(0x3CD, (i & 0x0f) | (i << 4)); + if (*VIDMEM == 0x5555AAAA) { + /* + * Seems like address wrap, but there could of course be + * 0x5555AAAA in here by accident, so we check with another + * pattern again. + */ + ET6K_SETSEG(segment); + /* test with other pattern again */ + *VIDMEM = 0xAAAA5555; + ET6K_SETSEG(i); + if (*VIDMEM == 0xAAAA5555) { + /* now we're sure: this is not real memory */ + fooled = TRUE; + break; + } + } + } + if (!fooled) { + real_ram = (segment + 1) * SEGSIZE; + break; + } + /* restore old contents again */ + ET6K_SETSEG(segment); + *VIDMEM = save_vidmem; + } + + /* restore original register contents */ + outb(0x3CD, oldSegSel1); + outb(0x3CB, oldSegSel2); + outb(0x3CE, 5); + outb(0x3CF, oldGR5); + outb(0x3CE, 6); + outb(0x3CF, oldGR6); + outb(0x3C4, 2); + outb(0x3C5, oldSEQ2); + outb(0x3C4, 4); + outb(0x3C5, oldSEQ4); + + vgaHWUnmapMem(pScrn); + return real_ram; +} + +/* + * Handle amount of allowed memory: some combinations can't use all + * available memory. Should we still allow the user to override this? + * + * This must be called AFTER the decision has been made to use linear mode + * and/or acceleration, or the memory limit code won't be able to work. + */ + +static int +TsengDoMemLimit(ScrnInfoPtr pScrn, int ram, int limit, char *reason) +{ + if (ram > limit) { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Only %d kb of memory can be used %s.\n", + limit, reason); + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Reducing video memory to %d kb.\n", limit); + ram = limit; + } + return ram; +} + +static int +TsengLimitMem(ScrnInfoPtr pScrn, int ram) +{ + TsengPtr pTseng = TsengPTR(pScrn); + + if (pTseng->UseLinMem && pTseng->Linmem_1meg) { + ram = TsengDoMemLimit(pScrn, ram, 1024, + "in linear mode on " + "this VGA board/bus configuration"); + } + if (pTseng->UseAccel && pTseng->UseLinMem) { + if (Is_W32_any) { + /* <= W32p_ab : + * 2 MB direct access + 2*512kb via apertures MBP0 and MBP1 + * == W32p_cd : + * 2*1MB via apertures MBP0 and MBP1 + */ + if (Is_W32p_cd) + ram = TsengDoMemLimit(pScrn, ram, 2048, + "in linear + accelerated mode " + "on W32p rev c and d"); + + ram = TsengDoMemLimit(pScrn, ram, 2048 + 1024, + "in linear + accelerated mode " + "on W32/W32i/W32p"); + + /* + * upper 516kb of 4MB linear map used for + * "externally mapped registers" + */ + ram = TsengDoMemLimit(pScrn, ram, 4096 - 516, + "in linear + accelerated mode " + "on W32/W32i/W32p"); + } + if (Is_ET6K) { + /* + * upper 8kb used for externally mapped and + * memory mapped registers + */ + ram = TsengDoMemLimit(pScrn, ram, 4096 - 8, + "in linear + accelerated mode " + "on ET6000/6100"); + } + } + ram = TsengDoMemLimit(pScrn, ram, 4096, "on any Tseng card"); + return ram; +} + +/* + * TsengDetectMem -- + * try to find amount of video memory installed. + * + */ + +static int +TsengDetectMem(ScrnInfoPtr pScrn) +{ + unsigned char config; + int ramtype = 0; + int ram = 0; + TsengPtr pTseng = TsengPTR(pScrn); + + PDEBUG(" TsengDetectMem\n"); + if (Is_ET6K) { + ramtype = inb(0x3C2) & 0x03; + switch (ramtype) { + case 0x03: /* MDRAM */ + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Video memory type: Multibank DRAM (MDRAM).\n"); + ram = ((inb(pTseng->IOAddress + 0x47) & 0x07) + 1) * 8 * 32; /* number of 8 32kb banks */ + if (inb(pTseng->IOAddress + 0x45) & 0x04) { + ram <<= 1; + } + /* + * 8*32kb MDRAM refresh control granularity in the ET6000 fails to + * recognize 2.25 MB of memory (detects 2.5 instead) + */ + ram = et6000_check_videoram(pScrn, ram); + break; + case 0x00: /* DRAM -- VERY unlikely on ET6000 cards, IMPOSSIBLE on ET6100 */ + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Video memory type: Standard DRAM.\n"); + ram = 1024 << (inb(pTseng->IOAddress + 0x45) & 0x03); + break; + default: /* unknown RAM type */ + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "Unknown ET6000 video memory type %d -- assuming 1 MB (unless specified)\n", + ramtype); + ram = 1024; + } + } else { /* pre-ET6000 devices */ + int iobase = VGAHWPTR(pScrn)->IOBase; + + outb(iobase + 0x04, 0x37); + config = inb(iobase + 0x05); + + ram = 128 << (config & 0x03); + + if (config & 0x80) + ram <<= 1; + + /* Check for interleaving on W32i/p. */ + if (Is_W32i || Is_W32p) { + outb(iobase + 0x04, 0x32); + config = inb(iobase + 0x05); + if (config & 0x80) { + ram <<= 1; + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Video memory type: Interleaved DRAM.\n"); + } else { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "Video memory type: Standard DRAM.\n"); + } + } + } + return ram; +} + +static Bool +TsengProcessHibit(ScrnInfoPtr pScrn) +{ + MessageType from = X_CONFIG; + int hibit_mode_width; + int iobase; + TsengPtr pTseng = TsengPTR(pScrn); + + PDEBUG(" TsengProcessHibit\n"); + if (xf86IsOptionSet(pTseng->Options, OPTION_HIBIT_HIGH)) { + if (xf86IsOptionSet(pTseng->Options, OPTION_HIBIT_LOW)) { + xf86Msg(X_ERROR, "\nOptions \"hibit_high\" and \"hibit_low\" are incompatible;\n"); + xf86Msg(X_ERROR, " specify only one (not both) in XFree86 configuration file\n"); + return FALSE; + } + pTseng->save_divide = 0x40; + } else if (xf86IsOptionSet(pTseng->Options, OPTION_HIBIT_HIGH)) { + pTseng->save_divide = 0; + } else { + from = X_PROBED; + /* first check to see if hibit is probed from low-res mode */ + iobase = VGAHWPTR(pScrn)->IOBase; + outb(iobase + 4, 1); + hibit_mode_width = inb(iobase + 5) + 1; + if (hibit_mode_width > 82) { + xf86Msg(X_WARNING, "Non-standard VGA text or graphics mode while probing for hibit:\n"); + xf86Msg(X_WARNING, " probed 'hibit' value may be wrong.\n"); + xf86Msg(X_WARNING, " Preferably run probe from 80x25 textmode,\n"); + xf86Msg(X_WARNING, " or specify correct value in XFree86 configuration file.\n"); + } + /* Check for initial state of divide flag */ + outb(0x3C4, 7); + pTseng->save_divide = inb(0x3C5) & 0x40; + } + xf86DrvMsg(pScrn->scrnIndex, from, "Initial ET4000 hibit state: %s\n", + pTseng->save_divide & 0x40 ? "high" : "low"); + return TRUE; +} + +static Bool +TsengProcessOptions(ScrnInfoPtr pScrn) +{ + MessageType from; + double real; + TsengPtr pTseng = TsengPTR(pScrn); + + PDEBUG(" TsengProcessOptions\n"); + + /* Collect all of the relevant option flags (fill in pScrn->options) */ + xf86CollectOptions(pScrn, NULL); + + /* Process the options */ + if (!(pTseng->Options = xalloc(sizeof(TsengOptions)))) + return FALSE; + memcpy(pTseng->Options, TsengOptions, sizeof(TsengOptions)); + xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, pTseng->Options); + + from = X_DEFAULT; + pTseng->HWCursor = FALSE; /* default */ + if (xf86GetOptValBool(pTseng->Options, OPTION_HW_CURSOR, &pTseng->HWCursor)) + from = X_CONFIG; + if (xf86ReturnOptValBool(pTseng->Options, OPTION_SW_CURSOR, FALSE)) { + from = X_CONFIG; + pTseng->HWCursor = FALSE; + } + if (pTseng->HWCursor && !Is_ET6K) { + xf86DrvMsg(pScrn->scrnIndex, from, + "Hardware Cursor not supported on this chipset\n"); + pTseng->HWCursor = FALSE; + } + + xf86DrvMsg(pScrn->scrnIndex, from, "Using %s cursor\n", + pTseng->HWCursor ? "HW" : "SW"); + + if (pScrn->bitsPerPixel >= 8) { + if (pTseng->ChipType != TYPE_ET4000) + pTseng->UseAccel = TRUE; + if (xf86ReturnOptValBool(pTseng->Options, OPTION_NOACCEL, FALSE)) { + pTseng->UseAccel = FALSE; + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Acceleration disabled\n"); + } + } else + pTseng->UseAccel = FALSE; /* 1bpp and 4bpp are always non-accelerated */ + + pTseng->SlowDram = FALSE; + if (xf86IsOptionSet(pTseng->Options, OPTION_SLOW_DRAM)) { + pTseng->SlowDram = TRUE; + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Using slow DRAM access\n"); + } + pTseng->MedDram = FALSE; + if (xf86IsOptionSet(pTseng->Options, OPTION_MED_DRAM)) { + pTseng->MedDram = TRUE; + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Using Medium-speed DRAM access\n"); + } + pTseng->FastDram = FALSE; + if (xf86IsOptionSet(pTseng->Options, OPTION_FAST_DRAM)) { + pTseng->FastDram = TRUE; + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Using fast DRAM access\n"); + } + if ((pTseng->SetW32Interleave = + xf86GetOptValBool(pTseng->Options, OPTION_W32_INTERLEAVE, &pTseng->W32Interleave)) ) + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Forcing W32p memory interleave %s.\n", + pTseng->W32Interleave ? "ON" : "OFF"); + if ((pTseng->SetPCIBurst = + xf86GetOptValBool(pTseng->Options, OPTION_PCI_BURST, &pTseng->PCIBurst)) ) + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Forcing PCI burst mode %s.\n", + pTseng->PCIBurst ? "ON" : "OFF"); + from = X_CONFIG; + if (xf86GetOptValBool(pTseng->Options, OPTION_LINEAR, &pTseng->UseLinMem)) { + /* check if linear mode is allowed */ + if (pTseng->UseLinMem) { + if (!CHIP_SUPPORTS_LINEAR) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Linear memory not supported on chipset \"%s\".\n", + pScrn->chipset); + pTseng->UseLinMem = FALSE; + } else if (!xf86LinearVidMem()) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "This operating system does not support a linear framebuffer.\n"); + pTseng->UseLinMem = FALSE; + } + } + } else { + /* option not specified: use defaults */ + from = X_DEFAULT; + if (pTseng->PciTag) + pTseng->UseLinMem = TRUE; /* use linear memory by default on PCI systems */ + else + pTseng->UseLinMem = FALSE; /* ... and banked on non-PCI systems */ + } + xf86DrvMsg(pScrn->scrnIndex, from, "Using %s Memory.\n", + (pTseng->UseLinMem) ? "linear" : "banked"); + + pTseng->ShowCache = FALSE; + if (xf86ReturnOptValBool(pTseng->Options, OPTION_SHOWCACHE, FALSE)) { + pTseng->ShowCache = TRUE; + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "(for debugging only:) Visible off-screen memory\n"); + } + pTseng->Legend = FALSE; + if (xf86ReturnOptValBool(pTseng->Options, OPTION_LEGEND, FALSE)) { + pTseng->Legend = TRUE; + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Using Legend pixel clock selection.\n"); + } + pTseng->NoClockchip = FALSE; + if (xf86ReturnOptValBool(pTseng->Options, OPTION_NOCLOCKCHIP, FALSE)) { + pTseng->NoClockchip = TRUE; + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Disabling clockchip programming.\n"); + } + /* + * Check_Tseng_ramdac() already set pScrn->progClock according to probed + * values. Override it if requested. + */ + if (pTseng->NoClockchip) + pScrn->progClock = FALSE; + + pTseng->UsePCIRetry = FALSE; + if (xf86ReturnOptValBool(pTseng->Options, OPTION_PCI_RETRY, FALSE)) { + pTseng->UsePCIRetry = TRUE; + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "PCI retry enabled\n"); + } + pTseng->MemClk = 0; + if (xf86GetOptValFreq(pTseng->Options, OPTION_SET_MCLK, OPTUNITS_MHZ, &real)) + pTseng->MemClk = (int)(real * 1000.0); + return TRUE; +} + +static Bool +TsengGetLinFbAddress(ScrnInfoPtr pScrn) +{ + MessageType from; + TsengPtr pTseng = TsengPTR(pScrn); + resRange range[] = { {ResExcMemBlock|ResBus,0,0},_END }; + + PDEBUG(" TsengGetLinFbAddress\n"); + + /* let config file override Base address */ + if (pTseng->pEnt->device->MemBase != 0) { + pTseng->LinFbAddress = pTseng->pEnt->device->MemBase; + from = X_CONFIG; + /* check for possible errors in given linear base address */ + if ((pTseng->LinFbAddress & (~pTseng->LinFbAddressMask)) != 0) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "MemBase out of range. Must be <= 0x%x on 0x%x boundary.\n", + pTseng->LinFbAddressMask, ~(pTseng->LinFbAddressMask | 0xFF000000) + 1); + pTseng->LinFbAddress &= ~pTseng->LinFbAddressMask; + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, " Clipping MemBase to: 0x%x.\n", + pTseng->LinFbAddress); + range[0].rBegin = pTseng->LinFbAddress; + range[0].rEnd = pTseng->LinFbAddress + 16 * 1024 * 1024; + if (xf86RegisterResources(pTseng->pEnt->index,range,ResNone)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + " Cannot register linear memory." + " Using banked mode instead.\n"); + pTseng->UseLinMem = FALSE; + return TRUE; + } + } + } else { + from = X_PROBED; + if (pTseng->PciTag) { + /* + * base0 is the framebuffer and base1 is the PCI IO space. + */ + if ((pTseng->PciInfo->memBase[0]) != 0) { + pTseng->LinFbAddress = pTseng->PciInfo->memBase[0]; + } else { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "No valid Framebuffer address in PCI config space;\n"); + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + " Falling back to banked mode.\n"); + pTseng->UseLinMem = FALSE; + return TRUE; + } + if (xf86RegisterResources(pTseng->pEnt->index,NULL,ResNone)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + " Cannot register linear memory." + " Using banked mode instead.\n"); + pTseng->UseLinMem = FALSE; + return TRUE; + } + } else { /* non-PCI boards */ + /* W32p cards can give us their Lin. memory address through the PCI + * configuration. For W32i, this is not possible (VL-bus, MCA or ISA). W32i + * cards have three extra external "address" lines, SEG2..SEG0 which _can_ + * be connected to any set of address lines in addition to the already + * connected A23..A0. SEG2..SEG0 are either for three extra address lines + * or to connect an external address decoder (mostly an 74F27). It is NOT + * possible to know how SEG2..SEG0 are connected. We _assume_ they are + * connected to A26..A24 (most likely case). This means linear memory can + * be decoded into any 4MB block in the address range 0..128MB. + * + * For non-PCI cards (especially VLB), most motherboards don't decode all + * 32 address bits. The weird default memory base below will always end up + * at the end of the decoded address space -- independent of the number of + * address bits that are decoded. + */ +#define DEFAULT_LIN_MEMBASE ( (256 + 128 + 64 + 32 + 16 + 8 + 4) * 1024*1024 ) +#define DEFAULT_LIN_MEMBASE_PCI (DEFAULT_LIN_MEMBASE & 0xFF000000) + + switch (pTseng->ChipType) { + case TYPE_ET4000W32: + case TYPE_ET4000W32I: + case TYPE_ET4000W32P: /* A31,A30 are decoded as 00 (=always mapped below 512 MB) */ + pTseng->LinFbAddress = DEFAULT_LIN_MEMBASE; + if (pTseng->LinFbAddress > pTseng->LinFbAddressMask) /* ... except if we can't decode that much */ + pTseng->LinFbAddress = pTseng->LinFbAddressMask - 4 * 1024 * 1024; /* top of decodable memory */ + break; + default: + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "TsengNonPciLinMem(): Internal error. This should not happen: please report to XFree86@XFree86.Org\n"); + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + " Falling back to banked mode.\n"); + pTseng->UseLinMem = FALSE; + return TRUE; + } + pTseng->LinFbAddress &= pTseng->LinFbAddressMask; + + /* One final check for a valid MemBase */ + if (pTseng->LinFbAddress < 4096 * 1024) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Invalid MemBase for linear mode:\n"); + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + " please define a non-zero MemBase in XF86Config.\n"); + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + " See README.tseng or tseng.sgml for more information.\n"); + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + " Using banked mode instead.\n"); + pTseng->UseLinMem = FALSE; + return TRUE; + } + range[0].type |= ResBios; + range[0].rBegin = pTseng->LinFbAddress; + range[0].rEnd = pTseng->LinFbAddress + 16 * 1024 * 1024; + if (xf86RegisterResources(pTseng->pEnt->index,range,ResNone)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + " Cannot register linear memory." + " Using banked mode instead.\n"); + pTseng->UseLinMem = FALSE; + return TRUE; + } + } + } + + /* The W32 linear map address space is always 4Mb (mainly because the + * memory-mapped registers are located near the top of the 4MB area). + * The ET6000 maps out 16 Meg, but still uses only 4Mb of that. + * However, since all mmap()-ed space is also reflected in the "ps" + * listing for the Xserver, many users will be worried by a server that + * always eats 16MB of memory, even if it's not "real" memory, just + * address space. Not mapping all of the 16M may be a potential problem + * though: if another board is mapped on top of the remaining part of + * the 16M... Boom! + */ + if (Is_ET6K) + pTseng->FbMapSize = 16384 * 1024; + else + pTseng->FbMapSize = 4096 * 1024; + + xf86DrvMsg(pScrn->scrnIndex, from, "Linear framebuffer at 0x%lX\n", + (unsigned long)pTseng->LinFbAddress); + + return TRUE; +} + +static Bool +TsengPreInit(ScrnInfoPtr pScrn, int flags) +{ + TsengPtr pTseng; + MessageType from; + int i; + + if (flags & PROBE_DETECT) return FALSE; + + PDEBUG(" TsengPreInit\n"); + + /* + * Note: This function is only called once at server startup, and + * not at the start of each server generation. This means that + * only things that are persistent across server generations can + * be initialised here. xf86Screens[] is (pScrn is a pointer to one + * of these). Privates allocated using xf86AllocateScrnInfoPrivateIndex() + * are too, and should be used for data that must persist across + * server generations. + * + * Per-generation data should be allocated with + * AllocateScreenPrivateIndex() from the ScreenInit() function. + */ + + /* The vgahw module should be loaded here when needed */ + + /* This driver doesn't expect more than one entity per screen */ + if (pScrn->numEntities > 1) + return FALSE; + + /* Allocate the TsengRec driverPrivate */ + if (!TsengGetRec(pScrn)) { + return FALSE; + } + pTseng = TsengPTR(pScrn); + + /* This is the general case */ + pTseng->pEnt = xf86GetEntityInfo(*pScrn->entityList); + +#if 1 + if (xf86LoadSubModule(pScrn, "int10")) { + xf86Int10InfoPtr pInt; + xf86LoaderReqSymLists(int10Symbols, NULL); +#if 1 + xf86DrvMsg(pScrn->scrnIndex,X_INFO,"initializing int10\n"); + pInt = xf86InitInt10(pTseng->pEnt->index); + xf86FreeInt10(pInt); +#endif + } +#endif + + if (!xf86LoadSubModule(pScrn, "vgahw")) + return FALSE; + xf86LoaderReqSymLists(vgaHWSymbols, NULL); + /* + * Allocate a vgaHWRec + */ + if (!vgaHWGetHWRec(pScrn)) + return FALSE; + + vgaHWGetIOBase(VGAHWPTR(pScrn)); + /* + * Since, the capabilities are determined by the chipset, the very first + * thing to do is to figure out the chipset and its capabilities. + */ + + TsengUnlock(); + + /* + * Find the bus slot for this screen (PCI or other). This also finds the + * exact chipset type. + */ + /* This driver can handle ISA and PCI buses */ + if (pTseng->pEnt->location.type == BUS_PCI) { + pTseng->PciInfo = xf86GetPciInfoForEntity(pTseng->pEnt->index); + if (!TsengPreInitPCI(pScrn)) { + TsengFreeRec(pScrn); + return FALSE; + } + } else if (!TsengPreInitNoPCI(pScrn)) { + TsengFreeRec(pScrn); + return FALSE; + } + + /* + * Find RAMDAC and CLOCKCHIP type and fill Tsengdac struct + */ + if (!Check_Tseng_Ramdac(pScrn)) + return FALSE; + + /* check for clockchip */ + if (!Tseng_check_clockchip(pScrn)) { + return FALSE; + } + /* + * Now we can check what depth we support. + */ + + /* Set pScrn->monitor */ + pScrn->monitor = pScrn->confScreen->monitor; + + /* + * The first thing we should figure out is the depth, bpp, etc. + * Our default depth is 8, so pass it to the helper function. + * Our preference for depth 24 is 24bpp, so tell it that too. + */ + if (!xf86SetDepthBpp(pScrn, 8, 8, 8, Support24bppFb | Support32bppFb | + SupportConvert32to24 | PreferConvert32to24)) { + return FALSE; + } else { + /* Check that the returned depth is one we support */ + Bool CanDo16bpp = FALSE, CanDo24bpp = FALSE, CanDo32bpp = FALSE; + Bool CanDoThis = FALSE; + + switch (pTseng->DacInfo.DacType) { + case ET6000_DAC: + case ICS5341_DAC: + case STG1703_DAC: + case STG1702_DAC: + CanDo16bpp = TRUE; + CanDo24bpp = TRUE; + CanDo32bpp = TRUE; + break; + case ATT20C490_DAC: + case ATT20C491_DAC: + case ATT20C492_DAC: + case ATT20C493_DAC: + case ICS5301_DAC: + case MUSIC4910_DAC: + CanDo16bpp = TRUE; + CanDo24bpp = TRUE; + break; + case CH8398_DAC: + CanDo16bpp = TRUE; + CanDo24bpp = TRUE; + break; + case STG1700_DAC: /* can't do packed 24bpp over a 16-bit bus */ + CanDo16bpp = TRUE; /* FIXME: can do it over 8 bit bus */ + CanDo32bpp = TRUE; + break; + default: /* default: only 1, 4, 8 bpp */ + break; + } + + switch (pScrn->depth) { + case 1: + case 4: + case 8: + CanDoThis = TRUE; + break; + case 16: + CanDoThis = CanDo16bpp; + break; + case 24: + CanDoThis = (CanDo24bpp || CanDo32bpp); + break; + } + if (!CanDoThis) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Given depth (%d) is not supported by this chipset/RAMDAC\n", + pScrn->depth); + return FALSE; + } + if ((pScrn->bitsPerPixel == 32) && (!CanDo32bpp)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Given bpp (%d) is not supported by this chipset/RAMDAC\n", + pScrn->bitsPerPixel); + return FALSE; + } + } + xf86PrintDepthBpp(pScrn); + + /* Get the depth24 pixmap format */ + if (pScrn->depth == 24 && pix24bpp == 0) + pix24bpp = xf86GetBppFromDepth(pScrn, 24); + + if (pScrn->bitsPerPixel > 8) + pTseng->Bytesperpixel = pScrn->bitsPerPixel / 8; + else + pTseng->Bytesperpixel = 1; /* this is fake for < 8bpp, but simplifies other code */ + + /* hardware limits */ + pScrn->maxHValue = Tseng_HMAX; + pScrn->maxVValue = Tseng_VMAX; + + /* + * This must happen after pScrn->display has been set because + * xf86SetWeight references it. + */ + + /* Set weight/mask/offset for depth > 8 */ + if (pScrn->depth > 8) { + /* The defaults are OK for us */ + rgb zeros = {0, 0, 0}; + rgb mask; + + /* + * Initialize mask here. + * Currently we only treat the ICS5341 RAMDAC special + */ + if ((pScrn->depth == 24) && (pScrn->bitsPerPixel == 24)) + mask = pTseng->DacInfo.rgb24packed; + else + mask = zeros; + + if (!xf86SetWeight(pScrn, zeros, mask)) { + return FALSE; + } else { + /* XXX check that weight returned is supported */ + ; + } + } + /* Set the default visual. */ + if (!xf86SetDefaultVisual(pScrn, -1)) + return FALSE; + + /* The gamma fields must be initialised when using the new cmap code */ + if (pScrn->depth > 1) { + Gamma zeros = {0.0, 0.0, 0.0}; + + if (!xf86SetGamma(pScrn, zeros)) { + return FALSE; + } + } + + /* Set the bits per RGB for 8bpp mode */ + if (pScrn->depth == 8) { + /* Default to 6, because most Tseng chips/RAMDACs don't support it */ + pScrn->rgbBits = 6; + } + if (!TsengProcessOptions(pScrn)) /* must be done _after_ we know what chip this is */ + return FALSE; + + if (pTseng->UseLinMem) { + if (!TsengGetLinFbAddress(pScrn)) + return FALSE; + } + + if (pTseng->UseAccel) + VGAHWPTR(pScrn)->MapSize = 0x20000; /* accelerator apertures and MMIO */ + else + VGAHWPTR(pScrn)->MapSize = 0x10000; + + if (pTseng->UseLinMem) { + /* + * XXX At least part of this range does appear to be disabled, + * but to play safe, it is marked as "unused" for now. + * Changed this to "disable". Otherwise it might interfere with DGA. + */ + xf86SetOperatingState(resVgaMem, pTseng->pEnt->index, ResDisableOpr); + } + + /* hibit processing (TsengProcessOptions() must have been called first) */ + pTseng->save_divide = 0x40; /* default */ + if (!Is_ET6K) { + if (!TsengProcessHibit(pScrn)) + return FALSE; + } + /* + * If the user has specified the amount of memory in the XF86Config + * file, we respect that setting. + */ + if (pTseng->pEnt->device->videoRam != 0) { + pScrn->videoRam = pTseng->pEnt->device->videoRam; + from = X_CONFIG; + } else { + from = X_PROBED; + pScrn->videoRam = TsengDetectMem(pScrn); + } + pScrn->videoRam = TsengLimitMem(pScrn, pScrn->videoRam); + + xf86DrvMsg(pScrn->scrnIndex, from, "VideoRAM: %d kByte.\n", + pScrn->videoRam); + + /* do all clock-related setup */ + tseng_clock_setup(pScrn); + + /* + * xf86ValidateModes will check that the mode HTotal and VTotal values + * don't exceed the chipset's limit if pScrn->maxHValue and + * pScrn->maxVValue are set. Since our TsengValidMode() already takes + * care of this, we don't worry about setting them here. + */ + + /* Select valid modes from those available */ + i = xf86ValidateModes(pScrn, pScrn->monitor->Modes, + pScrn->display->modes, pTseng->clockRange[0], + NULL, 32, pScrn->maxHValue, 8*pTseng->Bytesperpixel, /* H limits */ + 0, pScrn->maxVValue, /* V limits */ + pScrn->display->virtualX, + pScrn->display->virtualY, + pTseng->FbMapSize, + LOOKUP_BEST_REFRESH); /* LOOKUP_CLOSEST_CLOCK | LOOKUP_CLKDIV2 when no programmable clock ? */ + + if (i == -1) { + TsengFreeRec(pScrn); + return FALSE; + } + /* Prune the modes marked as invalid */ + xf86PruneDriverModes(pScrn); + + if (i == 0 || pScrn->modes == NULL) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "No valid modes found\n"); + TsengFreeRec(pScrn); + return FALSE; + } + /* + * Set the CRTC parameters for all of the modes based on the type + * of mode, and the chipset's interlace requirements. + * + * Calling this is required if the mode->Crtc* values are used by the + * driver and if the driver doesn't provide code to set them. They + * are not pre-initialised at all. + */ + xf86SetCrtcForModes(pScrn, 0); + + /* Set the current mode to the first in the list */ + pScrn->currentMode = pScrn->modes; + + /* Print the list of modes being used */ + xf86PrintModes(pScrn); + + /* Set display resolution */ + xf86SetDpi(pScrn, 0, 0); + + /* Load bpp-specific modules */ + switch (pScrn->bitsPerPixel) { + case 1: + if (xf86LoadSubModule(pScrn, "xf1bpp") == NULL) { + TsengFreeRec(pScrn); + return FALSE; + } + xf86LoaderReqSymbols("xf1bppScreenInit", NULL); + break; + case 4: + if (xf86LoadSubModule(pScrn, "xf4bpp") == NULL) { + TsengFreeRec(pScrn); + return FALSE; + } + xf86LoaderReqSymbols("xf4bppScreenInit", NULL); + break; + default: + if (xf86LoadSubModule(pScrn, "fb") == NULL) { + TsengFreeRec(pScrn); + return FALSE; + } + xf86LoaderReqSymLists(fbSymbols, NULL); + break; + } + + /* Load XAA if needed */ + if (pTseng->UseAccel) { + if (!xf86LoadSubModule(pScrn, "xaa")) { + TsengFreeRec(pScrn); + return FALSE; + } + xf86LoaderReqSymLists(xaaSymbols, NULL); + } + /* Load ramdac if needed */ + if (pTseng->HWCursor) { + if (!xf86LoadSubModule(pScrn, "ramdac")) { + TsengFreeRec(pScrn); + return FALSE; + } + xf86LoaderReqSymLists(ramdacSymbols, NULL); + } +/* TsengLock(); */ + + return TRUE; +} + +static void +TsengSetupAccelMemory(int scrnIndex, ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + TsengPtr pTseng = TsengPTR(pScrn); + int offscreen_videoram, videoram_end, req_videoram; + int i; + int v; + + /* XXX Hack to suppress messages in subsequent generations. */ + if (serverGeneration == 1) + v = 1; + else + v = 100; + /* + * The accelerator requires free off-screen video memory to operate. The + * more there is, the more it can accelerate. + */ + + videoram_end = pScrn->videoRam * 1024; + offscreen_videoram = videoram_end - + pScrn->displayWidth * pScrn->virtualY * pTseng->Bytesperpixel; + xf86DrvMsgVerb(scrnIndex, X_INFO, v, "Available off-screen memory: %d bytes.\n", + offscreen_videoram); + + /* + * The HW cursor requires 1kb of off-screen memory, aligned to 1kb + * (256 DWORDS). Setting up its memory first ensures the alignment. + */ + if (pTseng->HWCursor) { + req_videoram = 1024; + if (offscreen_videoram < req_videoram) { + xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, v, + "Hardware Cursor disabled. It requires %d bytes of free video memory\n", + req_videoram); + pTseng->HWCursor = FALSE; + pTseng->HWCursorBufferOffset = 0; + } else { + offscreen_videoram -= req_videoram; + videoram_end -= req_videoram; + pTseng->HWCursorBufferOffset = videoram_end; + } + } else { + pTseng->HWCursorBufferOffset = 0; + } + + /* + * Acceleration memory setup. Do this only if acceleration is enabled. + */ + if (!pTseng->UseAccel) return; + + /* + * Basic acceleration needs storage for FG, BG and PAT colors in + * off-screen memory. Each color requires 2(ping-pong)*8 bytes. + */ + req_videoram = 2 * 8 * 3; + if (offscreen_videoram < req_videoram) { + xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, v, + "Acceleration disabled. It requires AT LEAST %d bytes of free video memory\n", + req_videoram); + pTseng->UseAccel = FALSE; + pTseng->AccelColorBufferOffset = 0; + goto end_memsetup; /* no basic acceleration means none at all */ + } else { + offscreen_videoram -= req_videoram; + videoram_end -= req_videoram; + pTseng->AccelColorBufferOffset = videoram_end; + } + + /* + * Color expansion (using triple buffering) requires 3 non-expanded + * scanlines, DWORD padded. + */ + req_videoram = 3 * ((pScrn->virtualX + 31) / 32) * 4; + if (offscreen_videoram < req_videoram) { + xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, v, + "Accelerated color expansion disabled (%d more bytes of free video memory required)\n", + req_videoram - offscreen_videoram); + pTseng->AccelColorExpandBufferOffsets[0] = 0; + } else { + offscreen_videoram -= req_videoram; + for (i = 0; i < 3; i++) { + videoram_end -= req_videoram / 3; + pTseng->AccelColorExpandBufferOffsets[i] = videoram_end; + } + } + + /* + * XAA ImageWrite support needs two entire line buffers. The + * current code assumes buffer 1 lies in the same 8kb aperture as + * buffer 0. + * + * [ FIXME: aren't we forgetting the DWORD padding here ? ] + * [ FIXME: why here double-buffering and in colexp triple-buffering? ] + */ + req_videoram = 2 * (pScrn->virtualX * pTseng->Bytesperpixel); + /* banked mode uses an 8kb aperture for imagewrite */ + if ((req_videoram > 8192) && (!pTseng->UseLinMem)) { + xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, v, + "Accelerated ImageWrites disabled (banked %dbpp virtual width must be <= %d)\n", + pScrn->bitsPerPixel, 8192 / (2 * pTseng->Bytesperpixel)); + pTseng->AccelImageWriteBufferOffsets[0] = 0; + } else if (offscreen_videoram < req_videoram) { + xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, v, + "Accelerated ImageWrites disabled (%d more bytes of free video memory required)\n", + req_videoram - offscreen_videoram); + pTseng->AccelImageWriteBufferOffsets[0] = 0; + } else { + offscreen_videoram -= req_videoram; + for (i = 0; i < 2; i++) { + videoram_end -= req_videoram / 2; + pTseng->AccelImageWriteBufferOffsets[i] = videoram_end; + } + } + + xf86DrvMsgVerb(scrnIndex, X_INFO, v, + "Remaining off-screen memory available for pixmap cache: %d bytes.\n", + offscreen_videoram); + +end_memsetup: + pScrn->videoRam = videoram_end / 1024; +} + +static Bool +TsengScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv) +{ + ScrnInfoPtr pScrn; + TsengPtr pTseng; + int ret; + VisualPtr visual; + + PDEBUG(" TsengScreenInit\n"); + + /* + * First get the ScrnInfoRec + */ + pScrn = xf86Screens[pScreen->myNum]; + + pTseng = TsengPTR(pScrn); + /* Map the Tseng memory areas */ + if (!TsengMapMem(pScrn)) + return FALSE; + + /* Save the current state */ + TsengSave(pScrn); + + /* Initialise the first mode */ + TsengModeInit(pScrn, pScrn->currentMode); + + /* Darken the screen for aesthetic reasons and set the viewport */ + TsengSaveScreen(pScreen, SCREEN_SAVER_ON); + + TsengAdjustFrame(scrnIndex, pScrn->frameX0, pScrn->frameY0, 0); + /* XXX Fill the screen with black */ + + /* + * Reset visual list. + */ + miClearVisualTypes(); + + /* Setup the visuals we support. */ + + /* + * For bpp > 8, the default visuals are not acceptable because we only + * support TrueColor and not DirectColor. To deal with this, call + * miSetVisualTypes for each visual supported. + */ + if (!miSetVisualTypes(pScrn->depth, miGetDefaultVisualMask(pScrn->depth), + pScrn->rgbBits, pScrn->defaultVisual)) + return FALSE; + + miSetPixmapDepths (); + + /* + * Call the framebuffer layer's ScreenInit function, and fill in other + * pScreen fields. + */ + + switch (pScrn->bitsPerPixel) { + case 1: + ret = xf1bppScreenInit(pScreen, pTseng->FbBase, + pScrn->virtualX, pScrn->virtualY, + pScrn->xDpi, pScrn->yDpi, + pScrn->displayWidth); + break; + case 4: + ret = xf4bppScreenInit(pScreen, pTseng->FbBase, + pScrn->virtualX, pScrn->virtualY, + pScrn->xDpi, pScrn->yDpi, + pScrn->displayWidth); + break; + default: + ret = fbScreenInit(pScreen, pTseng->FbBase, + pScrn->virtualX, pScrn->virtualY, + pScrn->xDpi, pScrn->yDpi, + pScrn->displayWidth, pScrn->bitsPerPixel); + break; + } + + if (!ret) + return FALSE; + + xf86SetBlackWhitePixels(pScreen); + + if (pScrn->bitsPerPixel > 8) { + /* Fixup RGB ordering */ + visual = pScreen->visuals + pScreen->numVisuals; + while (--visual >= pScreen->visuals) { + if ((visual->class | DynamicClass) == DirectColor) { + visual->offsetRed = pScrn->offset.red; + visual->offsetGreen = pScrn->offset.green; + visual->offsetBlue = pScrn->offset.blue; + visual->redMask = pScrn->mask.red; + visual->greenMask = pScrn->mask.green; + visual->blueMask = pScrn->mask.blue; + } + } + } + + /* must be after RGB ordering fixed */ + if (pScrn->bitsPerPixel > 4) + fbPictureInit(pScreen, 0, 0); + + if (pScrn->depth >= 8) + TsengDGAInit(pScreen); + + /* + * If banking is needed, initialise an miBankInfoRec (defined in + * "mibank.h"), and call miInitializeBanking(). + */ + + if (!pTseng->UseLinMem) { + if (!Is_stdET4K && (pScrn->videoRam > 1024)) { + pTseng->BankInfo.SetSourceBank = ET4000W32SetRead; + pTseng->BankInfo.SetDestinationBank = ET4000W32SetWrite; + pTseng->BankInfo.SetSourceAndDestinationBanks = ET4000W32SetReadWrite; + } else { + pTseng->BankInfo.SetSourceBank = ET4000SetRead; + pTseng->BankInfo.SetDestinationBank = ET4000SetWrite; + pTseng->BankInfo.SetSourceAndDestinationBanks = ET4000SetReadWrite; + } + pTseng->BankInfo.pBankA = pTseng->FbBase; + pTseng->BankInfo.pBankB = pTseng->FbBase; + pTseng->BankInfo.BankSize = 0x10000; + pTseng->BankInfo.nBankDepth = (pScrn->depth == 4) ? 1 : pScrn->depth; + + if (!miInitializeBanking(pScreen, pScrn->virtualX, pScrn->virtualY, + pScrn->displayWidth, &pTseng->BankInfo)) { + return FALSE; + } + } + + /* + * Initialize the acceleration interface. + */ + TsengSetupAccelMemory(scrnIndex, pScreen); + if (pTseng->UseAccel) { + tseng_init_acl(pScrn); /* set up accelerator */ + if (!TsengXAAInit(pScreen)) { /* set up XAA interface */ + return FALSE; + } + } + + miInitializeBackingStore(pScreen); + xf86SetSilkenMouse(pScreen); + /* Initialise cursor functions */ + miDCInitialize(pScreen, xf86GetPointerScreenFuncs()); + + /* Hardware Cursor layer */ + if (pTseng->HWCursor) { + if (!TsengHWCursorInit(pScreen)) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Hardware cursor initialization failed\n"); + } + + /* Initialise default colourmap */ + if (!miCreateDefColormap(pScreen)) + return FALSE; + + if (pScrn->depth == 4 || pScrn->depth == 8) { /* fb and xf4bpp */ + vgaHWHandleColormaps(pScreen); + } + pScrn->racIoFlags = RAC_FB | RAC_COLORMAP | RAC_CURSOR | RAC_VIEWPORT; + pScrn->racMemFlags = pScrn->racIoFlags; + + /* Wrap the current CloseScreen and SaveScreen functions */ + pScreen->SaveScreen = TsengSaveScreen; + + /* Support for DPMS, the ET4000W32Pc and newer uses a different and + * simpler method than the older cards. + */ + if (Is_W32p_cd || Is_ET6K) { + xf86DPMSInit(pScreen, (DPMSSetProcPtr)TsengCrtcDPMSSet, 0); + } else { + xf86DPMSInit(pScreen, (DPMSSetProcPtr)TsengHVSyncDPMSSet, 0); + } + + pTseng->CloseScreen = pScreen->CloseScreen; + pScreen->CloseScreen = TsengCloseScreen; + + /* Report any unused options (only for the first generation) */ + if (serverGeneration == 1) { + xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options); + } + /* Done */ + return TRUE; +} + +static Bool +TsengEnterVT(int scrnIndex, int flags) +{ + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + TsengPtr pTseng = TsengPTR(pScrn); + + PDEBUG(" TsengEnterVT\n"); + + vgaHWUnlock(VGAHWPTR(pScrn)); + TsengUnlock(); + + if (!TsengModeInit(pScrn, pScrn->currentMode)) + return FALSE; + if (pTseng->UseAccel) { + tseng_init_acl(pScrn); /* set up accelerator */ + } + return TRUE; +} + +static void +TsengLeaveVT(int scrnIndex, int flags) +{ + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + TsengPtr pTseng = TsengPTR(pScrn); + + PDEBUG(" TsengLeaveVT\n"); + TsengRestore(pScrn, &(VGAHWPTR(pScrn)->SavedReg), + &pTseng->SavedReg,VGA_SR_ALL); + + TsengLock(); + vgaHWLock(VGAHWPTR(pScrn)); +} + +static Bool +TsengCloseScreen(int scrnIndex, ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + TsengPtr pTseng = TsengPTR(pScrn); + + PDEBUG(" TsengCloseScreen\n"); + + if (pScrn->vtSema) { + TsengRestore(pScrn, &(VGAHWPTR(pScrn)->SavedReg), + &(pTseng->SavedReg),VGA_SR_ALL); + TsengUnmapMem(pScrn); + } + if (pTseng->AccelInfoRec) + XAADestroyInfoRec(pTseng->AccelInfoRec); + if (pTseng->CursorInfoRec) + xf86DestroyCursorInfoRec(pTseng->CursorInfoRec); + + pScrn->vtSema = FALSE; + + pScreen->CloseScreen = pTseng->CloseScreen; + return (*pScreen->CloseScreen) (scrnIndex, pScreen); +} + +/* + * SaveScreen -- + * + * perform a sequencer reset. + * + * The ET4000 "Video System Configuration 1" register (CRTC index 0x36), + * which is used to set linear memory mode and MMU-related stuff, is + * partially reset to "0" when TS register index 0 bit 1 is set (synchronous + * reset): bits 3..5 are reset during a sync. reset. + * + * We therefor do _not_ call vgaHWSaveScreen here, since it does a sequencer + * reset. Instead, we do the same as in vgaHWSaveScreen except for the seq. reset. + * + * If this is not done, the higher level code will not be able to access the + * framebuffer (because it is temporarily in banked mode instead of linear + * mode) as long as SaveScreen is active (=in between a + * SaveScreen(FALSE)/SaveScreen(TRUE) pair) + */ + +static Bool +TsengSaveScreen(ScreenPtr pScreen, int mode) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + TsengPtr pTseng = TsengPTR(pScrn); + Bool unblank; + + PDEBUG(" TsengSaveScreen\n"); + + unblank = xf86IsUnblank(mode); + + if (Is_ET6K) { + return vgaHWSaveScreen(pScreen, unblank); + } else { + if (unblank) + SetTimeSinceLastInputEvent(); + + if (pScrn->vtSema) { + TsengBlankScreen(pScrn, unblank); + } + return (TRUE); + } +} + +static Bool +TsengMapMem(ScrnInfoPtr pScrn) +{ + TsengPtr pTseng = TsengPTR(pScrn); + + PDEBUG(" TsengMapMem\n"); + + /* Map the VGA memory */ + + if (!vgaHWMapMem(pScrn)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Could not mmap standard VGA memory aperture.\n"); + return FALSE; + } + + if (pTseng->UseLinMem) { + pTseng->FbBase = xf86MapPciMem(pScrn->scrnIndex, VIDMEM_FRAMEBUFFER, + pTseng->PciTag, + (unsigned long)pTseng->LinFbAddress, + pTseng->FbMapSize); + if (pTseng->FbBase == NULL) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Could not mmap linear video memory.\n"); + return FALSE; + } + if (pTseng->UseAccel) { + pTseng->MMioBase = xf86MapPciMem(pScrn->scrnIndex, + VIDMEM_MMIO, + pTseng->PciTag, + (unsigned long)pTseng->LinFbAddress, + pTseng->FbMapSize); + if (!pTseng->MMioBase) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Could not mmap mmio memory.\n"); + return FALSE; + } + pTseng->MMioBase += 0x3FFF00L; + } + } else { + vgaHWPtr hwp = VGAHWPTR(pScrn); + pTseng->FbBase = hwp->Base; + if (pTseng->UseAccel) { + pTseng->MMioBase = xf86MapPciMem(pScrn->scrnIndex, + VIDMEM_MMIO, + pTseng->PciTag, + (unsigned long)hwp->MapPhys, + hwp->MapSize); + if (!pTseng->MMioBase) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Could not mmap mmio memory.\n"); + return FALSE; + } + pTseng->MMioBase += 0x1FF00L; + } + } + + if (pTseng->FbBase == NULL) + return FALSE; + + return TRUE; +} + +static Bool +TsengUnmapMem(ScrnInfoPtr pScrn) +{ + TsengPtr pTseng = TsengPTR(pScrn); + + PDEBUG(" TsengUnmapMem\n"); + + if (pTseng->UseLinMem) { + xf86UnMapVidMem(pScrn->scrnIndex, (pointer) pTseng->FbBase, pTseng->FbMapSize); + } + vgaHWUnmapMem(pScrn); + + pTseng->FbBase = NULL; + + return TRUE; +} + +static void +TsengFreeScreen(int scrnIndex, int flags) +{ + PDEBUG(" TsengFreeScreen\n"); + if (xf86LoaderCheckSymbol("vgaHWFreeHWRec")) + vgaHWFreeHWRec(xf86Screens[scrnIndex]); + TsengFreeRec(xf86Screens[scrnIndex]); +} + +Bool +TsengModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode) +{ + vgaHWPtr hwp; + TsengPtr pTseng = TsengPTR(pScrn); + TsengRegPtr new = &(pTseng->ModeReg); + TsengRegPtr initial = &(pTseng->SavedReg); + int row_offset; + int min_n2; + int hdiv = 1, hmul = 1; + + PDEBUG(" TsengModeInit\n"); + + switch (mode->PrivFlags) { + case TSENG_MODE_PIXMUX: + case TSENG_MODE_DACBUS16: + hdiv = pTseng->clockRange[1]->ClockDivFactor; + hmul = pTseng->clockRange[1]->ClockMulFactor; + break; + default: + hdiv = pTseng->clockRange[0]->ClockDivFactor; + hmul = pTseng->clockRange[0]->ClockMulFactor; + } + + /* + * Modify mode timings accordingly + */ + if (!mode->CrtcHAdjusted) { + /* now divide and multiply the horizontal timing parameters as required */ + mode->CrtcHTotal = (mode->CrtcHTotal * hmul) / hdiv; + mode->CrtcHDisplay = (mode->CrtcHDisplay * hmul) / hdiv; + mode->CrtcHSyncStart = (mode->CrtcHSyncStart * hmul) / hdiv; + mode->CrtcHSyncEnd = (mode->CrtcHSyncEnd * hmul) / hdiv; + mode->CrtcHBlankStart = (mode->CrtcHBlankStart * hmul) / hdiv; + mode->CrtcHBlankEnd = (mode->CrtcHBlankEnd * hmul) / hdiv; + mode->CrtcHSkew = (mode->CrtcHSkew * hmul) / hdiv; + if (pScrn->bitsPerPixel == 24) { + int rgb_skew; + + /* + * in 24bpp, the position of the BLANK signal determines the + * phase of the R,G and B values. XFree86 sets blanking equal to + * the Sync, so setting the Sync correctly will also set the + * BLANK corectly, and thus also the RGB phase + */ + rgb_skew = (mode->CrtcHTotal / 8 - mode->CrtcHBlankEnd / 8 - 1) % 3; + mode->CrtcHBlankEnd += rgb_skew * 8 + 24; + /* HBlankEnd must come BEFORE HTotal */ + if (mode->CrtcHBlankEnd > mode->CrtcHTotal) + mode->CrtcHBlankEnd -= 24; + } + mode->CrtcHAdjusted = TRUE; + } + + /* set clockIndex to "2" for programmable clocks */ + if (pScrn->progClock) + mode->ClockIndex = 2; + + /* prepare standard VGA register contents */ + hwp = VGAHWPTR(pScrn); + if (!vgaHWInit(pScrn, mode)) + return (FALSE); + pScrn->vtSema = TRUE; + + /* prepare extended (Tseng) register contents */ + /* + * Start by copying all the saved registers in the "new" data, so we + * only have to modify those that need to change. + */ + + memcpy(new, initial, sizeof(TsengRegRec)); + + if (pScrn->bitsPerPixel < 8) { + /* Don't ask me why this is needed on the ET6000 and not on the others */ + if (Is_ET6K) + hwp->ModeReg.Sequencer[1] |= 0x04; + row_offset = hwp->ModeReg.CRTC[19]; + } else { + hwp->ModeReg.Attribute[16] = 0x01; /* use the FAST 256 Color Mode */ + row_offset = pScrn->displayWidth >> 3; /* overruled by 16/24/32 bpp code */ + } + + hwp->ModeReg.CRTC[20] = 0x60; + hwp->ModeReg.CRTC[23] = 0xAB; + new->ExtTS[6] = 0x00; + new->ExtTS[7] = 0xBC; + new->ExtCRTC[0x33] = 0x00; + + new->ExtCRTC[0x35] = (mode->Flags & V_INTERLACE ? 0x80 : 0x00) + | 0x10 + | ((mode->CrtcVSyncStart & 0x400) >> 7) + | (((mode->CrtcVDisplay - 1) & 0x400) >> 8) + | (((mode->CrtcVTotal - 2) & 0x400) >> 9) + | (((mode->CrtcVBlankStart - 1) & 0x400) >> 10); + + if (pScrn->bitsPerPixel < 8) + new->ExtATC = 0x00; + else + new->ExtATC = 0x80; + + if (pScrn->bitsPerPixel >= 8) { + if (pTseng->FastDram && !Is_ET6K) { + /* + * make sure Trsp is no more than 75ns + * Tcsw is 25ns + * Tcsp is 25ns + * Trcd is no more than 50ns + * Timings assume SCLK = 40MHz + * + * Note, this is experimental, but works for me (DHD) + */ + /* Tcsw, Tcsp, Trsp */ + new->ExtCRTC[0x32] &= ~0x1F; + if (new->ExtCRTC[0x32] & 0x18) + new->ExtCRTC[0x32] |= 0x08; + /* Trcd */ + new->ExtCRTC[0x32] &= ~0x20; + } + } + /* + * Here we make sure that CRTC regs 0x34 and 0x37 are untouched, except for + * some bits we want to change. + * Notably bit 7 of CRTC 0x34, which changes RAS setup time from 4 to 0 ns + * (performance), + * and bit 7 of CRTC 0x37, which changes the CRTC FIFO low treshold control. + * At really high pixel clocks, this will avoid lots of garble on the screen + * when something is being drawn. This only happens WAY beyond 80 MHz + * (those 135 MHz ramdac's...) + */ + if (Is_W32i || Is_W32p) { + if (!pTseng->SlowDram) + new->ExtCRTC[0x34] = (new->ExtCRTC[0x34] & 0x7F) | 0x80; + if ((mode->Clock * pTseng->Bytesperpixel) > 80000) + new->ExtCRTC[0x37] = (new->ExtCRTC[0x37] & 0x7f) | 0x80; + /* + * now on to the memory interleave setting (CR32 bit 7) + */ + if (pTseng->SetW32Interleave) { + if (pTseng->W32Interleave) + new->ExtCRTC[0x32] |= 0x80; + else + new->ExtCRTC[0x32] &= 0x7F; + } + } + if (Is_W32p) { + /* + * CR34 bit 4 controls the PCI Burst option + */ + if (pTseng->SetPCIBurst) { + if (pTseng->PCIBurst) + new->ExtCRTC[0x34] |= 0x10; + else + new->ExtCRTC[0x34] &= 0xEF; + } + } + + /* prepare clock-related registers when not Legend. + * cannot really SET the clock here yet, since the ET4000Save() + * is called LATER, so it would save the wrong state... + * ET4000Restore() is used to actually SET vga regs. + */ + + if (STG170x_programmable_clock || Gendac_programmable_clock) { + if (mode->PrivFlags == TSENG_MODE_PIXMUX) + /* pixmux requires a post-div of 4 on ICS GenDAC clock generator */ + min_n2 = 2; + else + min_n2 = 0; + TsengcommonCalcClock(mode->SynthClock, 1, 1, 31, min_n2, 3, + 100000, pTseng->max_vco_freq, + &(new->pll.f2_M), &(new->pll.f2_N)); + + new->pll.w_idx = 0; + new->pll.r_idx = 0; + + /* memory clock */ + if (Gendac_programmable_clock && pTseng->MClkInfo.Set) { + TsengcommonCalcClock(pTseng->MClkInfo.MemClk, 1, 1, 31, 1, 3, 100000, pTseng->MaxClock * 2 + 1, + &(new->pll.MClkM), &(new->pll.MClkN)); + } + } else if (ICD2061a_programmable_clock) { +#ifdef TODO + /* FIXME: icd2061_dwv not used anywhere ... */ + pTseng->icd2061_dwv = AltICD2061CalcClock(mode->SynthClock * 1000); + /* Tseng_ICD2061AClockSelect(mode->SynthClock); */ +#endif + } else if (CH8398_programmable_clock) { +#ifdef TODO + Chrontel8391CalcClock(mode->SynthClock, &temp1, &temp2, &temp3); + new->pll.f2_N = (unsigned char)(temp2); + new->pll.f2_M = (unsigned char)(temp1 | (temp3 << 6)); + /* ok LSB=f2_N and MSB=f2_M */ + /* now set the Clock Select Register(CSR) */ + new->pll.ctrl = (new->pll.ctrl | 0x90) & 0xF0; + new->pll.timingctrl &= 0x1F; + new->pll.r_idx = 0; + new->pll.w_idx = 0; +#endif + } else if (Is_ET6K) { + /* setting min_n2 to "1" will ensure a more stable clock ("0" is allowed though) */ + TsengcommonCalcClock(mode->SynthClock, 1, 1, 31, 1, 3, 100000, + pTseng->max_vco_freq, + &(new->pll.f2_M), &(new->pll.f2_N)); + /* above 130MB/sec, we enable the "LOW FIFO threshold" */ + if (mode->Clock * pTseng->Bytesperpixel > 130000) { + new->ExtET6K[0x41] |= 0x10; + if (Is_ET6100) + new->ExtET6K[0x46] |= 0x04; + } else { + new->ExtET6K[0x41] &= ~0x10; + if (Is_ET6100) + new->ExtET6K[0x46] &= ~0x04; + } + + if (pTseng->MClkInfo.Set) { + /* according to Tseng Labs, N1 must be <= 4, and N2 should always be 1 for MClk */ + TsengcommonCalcClock(pTseng->MClkInfo.MemClk, 1, 1, 4, 1, 1, + 100000, pTseng->MaxClock * 2, + &(new->pll.MClkM), &(new->pll.MClkN)); + } + /* + * Even when we don't allow setting the MClk value as described + * above, we can use the FAST/MED/SLOW DRAM options to set up + * the RAS/CAS delays as decided by the value of ExtET6K[0x44]. + * This is also a more correct use of the flags, as it describes + * how fast the RAM works. [HNH]. + */ + if (pTseng->FastDram) + new->ExtET6K[0x44] = 0x04; /* Fastest speed(?) */ + else if (pTseng->MedDram) + new->ExtET6K[0x44] = 0x15; /* Medium speed */ + else if (pTseng->SlowDram) + new->ExtET6K[0x44] = 0x35; /* Slow speed */ + else + ; /* keep current value */ + } + /* + * Set the clock selection bits. Because of the odd mapping between + * Tseng clock select bits and what XFree86 does, "CSx" refers to a + * register bit with the same name in the Tseng data books. + * + * XFree86 uses the following mapping: + * + * Tseng register bit name XFree86 clock select bit + * CS0 0 + * CS1 1 + * CS2 2 + * MCLK/2 3 + * CS3 4 + * CS4 not used + */ + if (mode->ClockIndex >= 0) { + /* CS0 and CS1 are set by standard VGA code (vgaHW) */ + /* CS2 = CRTC 0x34 bit 1 */ + new->ExtCRTC[0x34] = (new->ExtCRTC[0x34] & 0xFD) | + ((mode->ClockIndex & 0x04) >> 1); + /* for programmable clocks: disable MCLK/2 and MCLK/4 independent of hibit */ + new->ExtTS[7] = (new->ExtTS[7] & 0xBE); + if (!pScrn->progClock) { + /* clock select bit 3 = MCLK/2 disable/enable */ + new->ExtTS[7] |= (pTseng->save_divide ^ ((mode->ClockIndex & 0x08) << 3)); + } + /* clock select bit 4 = CS3 , clear CS4 */ + new->ExtCRTC[0x31] = ((mode->ClockIndex & 0x10) << 2) | (new->ExtCRTC[0x31] & 0x3F); + } + /* + * linear mode handling + */ + + if (Is_ET6K) { + if (pTseng->UseLinMem) { + new->ExtET6K[0x13] = pTseng->LinFbAddress >> 24; + new->ExtET6K[0x40] |= 0x09; + } else { + new->ExtET6K[0x40] &= ~0x09; + } + } else { /* et4000 style linear memory */ + if (pTseng->UseLinMem) { + new->ExtCRTC[0x36] |= 0x10; + if (Is_W32p || Is_ET6K) + new->ExtCRTC[0x30] = (pTseng->LinFbAddress >> 22) & 0xFF; + else + new->ExtCRTC[0x30] = ((pTseng->LinFbAddress >> 22) & 0x1F) ^ 0x1c; /* invert bits 4..2 */ + hwp->ModeReg.Graphics[6] &= ~0x0C; + new->ExtIMACtrl &= ~0x01; /* disable IMA port (to get >1MB lin mem) */ + } else { + new->ExtCRTC[0x36] &= ~0x10; + if (pTseng->ChipType < TYPE_ET4000W32P) + new->ExtCRTC[0x30] = 0x1C; /* default value */ + else + new->ExtCRTC[0x30] = 0x00; + } + } + + /* + * 16/24/32 bpp handling. + */ + + if (pScrn->bitsPerPixel >= 8) { + tseng_set_ramdac_bpp(pScrn, mode); + row_offset *= pTseng->Bytesperpixel; + } + /* + * Horizontal overflow settings: for modes with > 2048 pixels per line + */ + + hwp->ModeReg.CRTC[19] = row_offset; + new->ExtCRTC[0x3F] = ((((mode->CrtcHTotal >> 3) - 5) & 0x100) >> 8) + | ((((mode->CrtcHDisplay >> 3) - 1) & 0x100) >> 7) + | ((((mode->CrtcHBlankStart >> 3) - 1) & 0x100) >> 6) + | (((mode->CrtcHSyncStart >> 3) & 0x100) >> 4) + | ((row_offset & 0x200) >> 3) + | ((row_offset & 0x100) >> 1); + + /* + * Enable memory mapped IO registers when acceleration is needed. + */ + + if (pTseng->UseAccel) { + if (Is_ET6K) { + if (pTseng->UseLinMem) + new->ExtET6K[0x40] |= 0x02; /* MMU can't be used here (causes system hang...) */ + else + new->ExtET6K[0x40] |= 0x06; /* MMU is needed in banked accelerated mode */ + } else { + new->ExtCRTC[0x36] |= 0x28; + } + } + vgaHWUnlock(hwp); /* TODO: is this needed (tsengEnterVT does this) */ + /* Program the registers */ + TsengRestore(pScrn, &hwp->ModeReg, new, VGA_SR_MODE); + return TRUE; +} + +static Bool +TsengSwitchMode(int scrnIndex, DisplayModePtr mode, int flags) +{ + PDEBUG(" TsengSwitchMode\n"); + return TsengModeInit(xf86Screens[scrnIndex], mode); +} + +/* + * adjust the current video frame (viewport) to display the mousecursor. + */ +void +TsengAdjustFrame(int scrnIndex, int x, int y, int flags) +{ + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + TsengPtr pTseng = TsengPTR(pScrn); + int iobase = VGAHWPTR(pScrn)->IOBase; + int Base; + + PDEBUG(" TsengAdjustFrame\n"); + + if (pTseng->ShowCache) { + if (y) + y += 256; + } + if (pScrn->bitsPerPixel < 8) + Base = (y * pScrn->displayWidth + x + 3) >> 3; + else { + Base = ((y * pScrn->displayWidth + x + 1) * pTseng->Bytesperpixel) >> 2; + /* adjust Base address so it is a non-fractional multiple of pTseng->Bytesperpixel */ + Base -= (Base % pTseng->Bytesperpixel); + } + + outw(iobase + 4, (Base & 0x00FF00) | 0x0C); + outw(iobase + 4, ((Base & 0x00FF) << 8) | 0x0D); + outw(iobase + 4, ((Base & 0x0F0000) >> 8) | 0x33); + +} + +ModeStatus +TsengValidMode(int scrnIndex, DisplayModePtr mode, Bool verbose, int flags) +{ + + PDEBUG(" TsengValidMode\n"); + +#ifdef FIXME + is this needed? xf86ValidMode gets HMAX and VMAX variables, so it could deal with this. + need to recheck hsize with mode->Htotal*mulFactor/divFactor + /* Check for CRTC timing bits overflow. */ + if (mode->HTotal > Tseng_HMAX) { + return MODE_BAD_HVALUE; + } + if (mode->VTotal > Tseng_VMAX) { + return MODE_BAD_VVALUE; + } +#endif + + return MODE_OK; +} + +/* + * TsengSave -- + * save the current video mode + */ + +static void +TsengSave(ScrnInfoPtr pScrn) +{ + unsigned char temp, saveseg1 = 0, saveseg2 = 0; + TsengPtr pTseng = TsengPTR(pScrn); + vgaRegPtr vgaReg; + TsengRegPtr tsengReg; + int iobase = VGAHWPTR(pScrn)->IOBase; + + PDEBUG(" TsengSave\n"); + + vgaReg = &VGAHWPTR(pScrn)->SavedReg; + tsengReg = &pTseng->SavedReg; + + /* + * This function will handle creating the data structure and filling + * in the generic VGA portion. + */ + vgaHWSave(pScrn, vgaReg, VGA_SR_ALL); + + /* + * we need this here , cause we MUST disable the ROM SYNC feature + * this bit changed with W32p_rev_c... + */ + outb(iobase + 4, 0x34); + temp = inb(iobase + 5); + tsengReg->ExtCRTC[0x34] = temp; + if (Is_stdET4K || Is_W32_W32i || Is_W32p_ab) { +#ifdef OLD_CODE + outb(iobase + 5, temp & 0x1F); +#else + /* data books say translation ROM is controlled by bits 4 and 5 */ + outb(iobase + 5, temp & 0xCF); +#endif + } + + saveseg1 = inb(0x3CD); + outb(0x3CD, 0x00); /* segment select 1 */ + if (!Is_stdET4K) { + saveseg2 = inb(0x3CB); + outb(0x3CB, 0x00); /* segment select 2 */ + } + tsengReg->ExtSegSel[0] = saveseg1; + tsengReg->ExtSegSel[1] = saveseg2; + + outb(iobase + 4, 0x33); + tsengReg->ExtCRTC[0x33] = inb(iobase + 5); + outb(iobase + 4, 0x35); + tsengReg->ExtCRTC[0x35] = inb(iobase + 5); + if (Is_W32_any) { + outb(iobase + 4, 0x36); + tsengReg->ExtCRTC[0x36] = inb(iobase + 5); + outb(iobase + 4, 0x37); + tsengReg->ExtCRTC[0x37] = inb(iobase + 5); + outb(0x217a, 0xF7); + tsengReg->ExtIMACtrl = inb(0x217b); + } + if (!Is_ET6K) { + outb(iobase + 4, 0x32); + tsengReg->ExtCRTC[0x32] = inb(iobase + 5); + } + outb(0x3C4, 6); + tsengReg->ExtTS[6] = inb(0x3C5); + outb(0x3C4, 7); + tsengReg->ExtTS[7] = inb(0x3C5); + tsengReg->ExtTS[7] |= 0x14; + temp = inb(iobase + 0x0A); /* reset flip-flop */ + outb(0x3C0, 0x36); + tsengReg->ExtATC = inb(0x3C1); + outb(0x3C0, tsengReg->ExtATC); + if (DAC_is_GenDAC) { + /* Save GenDAC Command and PLL registers */ + outb(iobase + 4, 0x31); + temp = inb(iobase + 5); + outb(iobase + 5, temp | 0x40); + + tsengReg->pll.cmd_reg = inb(0x3c6); /* Enhanced command register */ + tsengReg->pll.w_idx = inb(0x3c8); /* PLL write index */ + tsengReg->pll.r_idx = inb(0x3c7); /* PLL read index */ + if (Gendac_programmable_clock) { + outb(0x3c7, 2); /* index to f2 reg */ + tsengReg->pll.f2_M = inb(0x3c9); /* f2 PLL M divider */ + tsengReg->pll.f2_N = inb(0x3c9); /* f2 PLL N1/N2 divider */ + outb(0x3c7, 10); /* index to Mclk reg */ +#ifdef TODO + tsengReg->MClkInfo.MClkM = inb(0x3c9); /* MClk PLL M divider */ + tsengReg->MClkInfo.MClkN = inb(0x3c9); /* MClk PLL N1/N2 divider */ +#endif + } + outb(0x3c7, 0x0e); /* index to PLL control */ + tsengReg->pll.ctrl = inb(0x3c9); /* PLL control */ + outb(iobase + 4, 0x31); + outb(iobase + 5, temp & ~0x40); + } + if ((pTseng->DacInfo.DacType == STG1702_DAC) || (pTseng->DacInfo.DacType == STG1703_DAC) + || (pTseng->DacInfo.DacType == STG1700_DAC)) { +#ifdef TODO + /* Save STG 1703 GenDAC Command and PLL registers + * unfortunately we reuse the gendac data structure, so the + * field names are not really good. + */ + + tseng_dactopel(); + tsengReg->pll.cmd_reg = tseng_getdaccomm(); /* Enhanced command register */ + if (STG170x_programmable_clock) { + tsengReg->pll.f2_M = STG1703getIndex(0x24); /* f2 PLL M divider */ + tsengReg->pll.f2_N = inb(0x3c6); /* f2 PLL N1/N2 divider */ + } + tsengReg->pll.ctrl = STG1703getIndex(0x03); /* pixel mode select control */ + tsengReg->pll.timingctrl = STG1703getIndex(0x05); /* pll timing control */ +#endif + } + if (DAC_IS_CHRONTEL) { + tseng_dactopel(); + tsengReg->pll.cmd_reg = tseng_getdaccomm(); + if (CH8398_programmable_clock) { + inb(0x3c8); + inb(0x3c6); + inb(0x3c6); + inb(0x3c6); + inb(0x3c6); + inb(0x3c6); + tsengReg->pll.timingctrl = inb(0x3c6); + /* Save PLL */ + outb(iobase + 4, 0x31); + temp = inb(iobase + 5); + outb(iobase + 5, temp | (1 << 6)); /* set RS2 through CS3 */ + /* We are in ClockRAM mode 0x3c7 = CRA, 0x3c8 = CWA, 0x3c9 = CDR */ + tsengReg->pll.r_idx = inb(0x3c7); + tsengReg->pll.w_idx = inb(0x3c8); + outb(0x3c7, 10); + tsengReg->pll.f2_N = inb(0x3c9); + tsengReg->pll.f2_M = inb(0x3c9); + outb(0x3c7, tsengReg->pll.r_idx); + inb(0x3c8); /* loop to Clock Select Register */ + inb(0x3c8); + inb(0x3c8); + inb(0x3c8); + tsengReg->pll.ctrl = inb(0x3c8); + outb(iobase + 4, 0x31); + outb(iobase + 5, temp); + } + } + if (ET6000_programmable_clock) { + /* Save ET6000 CLKDAC PLL registers */ + temp = inb(pTseng->IOAddress + 0x67); /* remember old CLKDAC index register pointer */ + outb(pTseng->IOAddress + 0x67, 2); + tsengReg->pll.f2_M = inb(pTseng->IOAddress + 0x69); + tsengReg->pll.f2_N = inb(pTseng->IOAddress + 0x69); + /* save MClk values */ + outb(pTseng->IOAddress + 0x67, 10); + tsengReg->pll.MClkM = inb(pTseng->IOAddress + 0x69); + tsengReg->pll.MClkN = inb(pTseng->IOAddress + 0x69); + /* restore old index register */ + outb(pTseng->IOAddress + 0x67, temp); + } + if (DAC_IS_ATT49x) + tsengReg->ATTdac_cmd = tseng_getdaccomm(); + + if (Is_ET6K) { + tsengReg->ExtET6K[0x13] = inb(pTseng->IOAddress + 0x13); + tsengReg->ExtET6K[0x40] = inb(pTseng->IOAddress + 0x40); + tsengReg->ExtET6K[0x58] = inb(pTseng->IOAddress + 0x58); + tsengReg->ExtET6K[0x41] = inb(pTseng->IOAddress + 0x41); + tsengReg->ExtET6K[0x44] = inb(pTseng->IOAddress + 0x44); + tsengReg->ExtET6K[0x46] = inb(pTseng->IOAddress + 0x46); + } + outb(iobase + 4, 0x30); + tsengReg->ExtCRTC[0x30] = inb(iobase + 5); + outb(iobase + 4, 0x31); + tsengReg->ExtCRTC[0x31] = inb(iobase + 5); + outb(iobase + 4, 0x3F); + tsengReg->ExtCRTC[0x3F] = inb(iobase + 5); +} + +/* + * TsengRestore -- + * restore a video mode + */ + +static void +TsengRestore(ScrnInfoPtr pScrn, vgaRegPtr vgaReg, TsengRegPtr tsengReg, + int flags) +{ + vgaHWPtr hwp; + TsengPtr pTseng; + unsigned char tmp; + int iobase = VGAHWPTR(pScrn)->IOBase; + + PDEBUG(" TsengRestore\n"); + + hwp = VGAHWPTR(pScrn); + pTseng = TsengPTR(pScrn); + + TsengProtect(pScrn, TRUE); + + outb(0x3CD, 0x00); /* segment select bits 0..3 */ + if (!Is_stdET4K) + outb(0x3CB, 0x00); /* segment select bits 4,5 */ + + if (DAC_is_GenDAC) { + /* Restore GenDAC Command and PLL registers */ + outb(iobase + 4, 0x31); + tmp = inb(iobase + 5); + outb(iobase + 5, tmp | 0x40); + + outb(0x3c6, tsengReg->pll.cmd_reg); /* Enhanced command register */ + + if (Gendac_programmable_clock) { + outb(0x3c8, 2); /* index to f2 reg */ + outb(0x3c9, tsengReg->pll.f2_M); /* f2 PLL M divider */ + outb(0x3c9, tsengReg->pll.f2_N); /* f2 PLL N1/N2 divider */ +#ifdef TODO + if (pTseng->MClkInfo.Set) { + outb(0x3c7, 10); /* index to Mclk reg */ + outb(0x3c9, tsengReg->MClkM); /* MClk PLL M divider */ + outb(0x3c9, tsengReg->MClkN); /* MClk PLL N1/N2 divider */ + } +#endif + } + outb(0x3c8, 0x0e); /* index to PLL control */ + outb(0x3c9, tsengReg->pll.ctrl); /* PLL control */ + outb(0x3c8, tsengReg->pll.w_idx); /* PLL write index */ + outb(0x3c7, tsengReg->pll.r_idx); /* PLL read index */ + + outb(iobase + 4, 0x31); + outb(iobase + 5, tmp & ~0x40); + } + if (DAC_is_STG170x) { +#ifdef TODO + /* Restore STG 170x GenDAC Command and PLL registers + * we share one data structure with the gendac code, so the names + * are not too good. + */ + + if (STG170x_programmable_clock) { + STG1703setIndex(0x24, tsengReg->pll.f2_M); + outb(0x3c6, tsengReg->pll.f2_N); /* use autoincrement */ + } + STG1703setIndex(0x03, tsengReg->pll.ctrl); /* primary pixel mode */ + outb(0x3c6, tsengReg->pll.ctrl); /* secondary pixel mode */ + outb(0x3c6, tsengReg->pll.timingctrl); /* pipeline timing control */ + usleep(500); /* 500 usec PLL settling time required */ + + STG1703magic(0); + tseng_dactopel(); + tseng_setdaccomm(tsengReg->pll.cmd_reg); /* write enh command reg */ +#endif + } + if (DAC_IS_CHRONTEL) { + tseng_dactopel(); + tseng_setdaccomm(tsengReg->pll.cmd_reg); + inb(0x3c8); + inb(0x3c6); + inb(0x3c6); + inb(0x3c6); + inb(0x3c6); + inb(0x3c6); + outb(0x3c6, tsengReg->pll.timingctrl); + if (CH8398_programmable_clock) { + outb(iobase + 4, 0x31); + tmp = inb(iobase + 5); + outb(iobase + 5, tmp | (1 << 6)); /* Set RS2 through CS3 */ + /* We are in ClockRAM mode 0x3c7 = CRA, 0x3c8 = CWA, 0x3c9 = CDR */ + outb(0x3c7, tsengReg->pll.r_idx); + outb(0x3c8, 10); + outb(0x3c9, tsengReg->pll.f2_N); + outb(0x3c9, tsengReg->pll.f2_M); + outb(0x3c8, tsengReg->pll.w_idx); + usleep(500); + inb(0x3c7); /* reset sequence */ + inb(0x3c8); /* loop to Clock Select Register */ + inb(0x3c8); + inb(0x3c8); + inb(0x3c8); + outb(0x3c8, tsengReg->pll.ctrl); + outb(iobase + 4, 0x31); + outb(iobase + 5, (tmp & 0x3F)); + } + } + if (ET6000_programmable_clock) { + /* Restore ET6000 CLKDAC PLL registers */ + tmp = inb(pTseng->IOAddress + 0x67); /* remember old CLKDAC index register pointer */ + outb(pTseng->IOAddress + 0x67, 2); + outb(pTseng->IOAddress + 0x69, tsengReg->pll.f2_M); + outb(pTseng->IOAddress + 0x69, tsengReg->pll.f2_N); + /* set MClk values if needed, but don't touch them if not needed */ + if (pTseng->MClkInfo.Set) { + /* + * Since setting the MClk to highly illegal value results in a + * total system crash, we'd better play it safe here. + * N1 must be <= 4, and N2 should always be 1 + */ + if ((tsengReg->pll.MClkN & 0xf8) != 0x20) { + xf86Msg(X_ERROR, "Internal Error in MClk registers: MClkM=0x%x, MClkN=0x%x\n", + tsengReg->pll.MClkM, tsengReg->pll.MClkN); + } else { + outb(pTseng->IOAddress + 0x67, 10); + outb(pTseng->IOAddress + 0x69, tsengReg->pll.MClkM); + outb(pTseng->IOAddress + 0x69, tsengReg->pll.MClkN); + } + } + /* restore old index register */ + outb(pTseng->IOAddress + 0x67, tmp); + } + if (DAC_IS_ATT49x) + tseng_setdaccomm(tsengReg->ATTdac_cmd); + + if (Is_ET6K) { + outb(pTseng->IOAddress + 0x13, tsengReg->ExtET6K[0x13]); + outb(pTseng->IOAddress + 0x40, tsengReg->ExtET6K[0x40]); + outb(pTseng->IOAddress + 0x58, tsengReg->ExtET6K[0x58]); + outb(pTseng->IOAddress + 0x41, tsengReg->ExtET6K[0x41]); + outb(pTseng->IOAddress + 0x44, tsengReg->ExtET6K[0x44]); + outb(pTseng->IOAddress + 0x46, tsengReg->ExtET6K[0x46]); + } + outw(iobase + 4, (tsengReg->ExtCRTC[0x3F] << 8) | 0x3F); + outw(iobase + 4, (tsengReg->ExtCRTC[0x30] << 8) | 0x30); + outw(iobase + 4, (tsengReg->ExtCRTC[0x31] << 8) | 0x31); + vgaHWRestore(pScrn, vgaReg, flags); /* TODO: does this belong HERE, in the middle? */ + outw(0x3C4, (tsengReg->ExtTS[6] << 8) | 0x06); + outw(0x3C4, (tsengReg->ExtTS[7] << 8) | 0x07); + tmp = inb(iobase + 0x0A); /* reset flip-flop */ + outb(0x3C0, 0x36); + outb(0x3C0, tsengReg->ExtATC); + outw(iobase + 4, (tsengReg->ExtCRTC[0x33] << 8) | 0x33); + outw(iobase + 4, (tsengReg->ExtCRTC[0x34] << 8) | 0x34); + outw(iobase + 4, (tsengReg->ExtCRTC[0x35] << 8) | 0x35); + if (Is_W32_any) { + outw(iobase + 4, (tsengReg->ExtCRTC[0x37] << 8) | 0x37); + outw(0x217a, (tsengReg->ExtIMACtrl << 8) | 0xF7); + } + if (!Is_ET6K) { + outw(iobase + 4, (tsengReg->ExtCRTC[0x32] << 8) | 0x32); + } + outb(0x3CD, tsengReg->ExtSegSel[0]); + if (pTseng->ChipType > TYPE_ET4000) + outb(0x3CB, tsengReg->ExtSegSel[1]); + +#ifdef TODO + /* + * This might be required for the Legend clock setting method, but + * should not be used for the "normal" case because the high order + * bits are not set in ClockIndex when returning to text mode. + */ + if (pTseng->Legend) { + if (tsengReg->ClockIndex >= 0) { + vgaProtect(TRUE); + (ClockSelect) (tsengReg->ClockIndex); + } +#endif + + TsengProtect(pScrn, FALSE); + + /* + * We must change CRTC 0x36 only OUTSIDE the TsengProtect(pScrn, + * TRUE)/TsengProtect(pScrn, FALSE) pair, because the sequencer reset + * also resets the linear mode bits in CRTC 0x36. + */ + if (Is_W32_any) { + outw(iobase + 4, (tsengReg->ExtCRTC[0x36] << 8) | 0x36); + } +} + +/* replacement of vgaHWBlankScreen(pScrn, unblank) without seq reset */ +void +TsengBlankScreen(ScrnInfoPtr pScrn, Bool unblank) +{ + unsigned char scrn; + PDEBUG(" TsengBlankScreen\n"); + + outb(0x3C4,1); + scrn = inb(0x3C5); + + if(unblank) { + scrn &= 0xDF; /* enable screen */ + }else { + scrn |= 0x20; /* blank screen */ + } + +/* vgaHWSeqReset(hwp, TRUE);*/ + outw(0x3C4, (scrn << 8) | 0x01); /* change mode */ +/* vgaHWSeqReset(hwp, FALSE);*/ +} + + +void +TsengProtect(ScrnInfoPtr pScrn, Bool on) +{ + PDEBUG(" TsengProtect\n"); + vgaHWProtect(pScrn, on); +} + + +/* + * The rest below is stuff from the old driver, which still needs to be + * checked and integrated in the ND + */ + +#ifdef OLD_DRIVER + +#include "tseng_cursor.h" +extern vgaHWCursorRec vgaHWCursor; + +/* + * ET4000Probe -- + * check whether a Et4000 based board is installed + */ + +static Bool +ET4000Probe() +{ + int numClocks; + Bool autodetect = TRUE; + + ... + + if (pScrn->bitsPerPixel >= 8) { + + ... + + /* + * Acceleration is only supported on W32 or newer chips. + * + * Also, some bus configurations only allow for a 1MB linear memory + * aperture instead of the default 4M aperture used on all Tseng devices. + * If acceleration is also enabled, you only get 512k + (with some aperture + * tweaking) 2*128k for a total of max 768 kb of memory. This just isn't + * worth having a lot of conditionals in the accelerator code (the + * memory-mapped registers move to the top of the 1M aperture), so we + * simply don't allow acceleration and linear mode combined on these cards. + * + */ + + if ((pTseng->ChipType < TYPE_ET4000W32) || (pTseng->Linmem_1meg && pTseng->UseLinMem)) { + tseng_use_ACL = FALSE; + } else { + /* enable acceleration-related options */ + OFLG_SET(OPTION_NOACCEL, &TSENG.ChipOptionFlags); + OFLG_SET(OPTION_PCI_RETRY, &TSENG.ChipOptionFlags); + OFLG_SET(OPTION_SHOWCACHE, &TSENG.ChipOptionFlags); + + tseng_use_ACL = !OFLG_ISSET(OPTION_NOACCEL, &vga256InfoRec.options); + } + + ... + + /* Hardware Cursor support */ +#ifdef W32_HW_CURSOR_FIXED + if (pTseng->ChipType >= TYPE_ET4000W32P) +#else + if (Is_ET6K) +#endif + { + /* Set HW Cursor option valid */ + OFLG_SET(OPTION_HW_CURSOR, &TSENG.ChipOptionFlags); + } + } + /* if (pScrn->bitsPerPixel >= 8) */ + else { + OFLG_CLR(OPTION_HW_CURSOR, &vga256InfoRec.options); + pTseng->UseLinMem = FALSE; + tseng_use_ACL = FALSE; + } + + if (!Is_ET6K) { + /* Initialize option flags allowed for this driver */ + OFLG_SET(OPTION_LEGEND, &TSENG.ChipOptionFlags); + OFLG_SET(OPTION_HIBIT_HIGH, &TSENG.ChipOptionFlags); + OFLG_SET(OPTION_HIBIT_LOW, &TSENG.ChipOptionFlags); + if (pScrn->bitsPerPixel >= 8) { + OFLG_SET(OPTION_PCI_BURST_ON, &TSENG.ChipOptionFlags); + OFLG_SET(OPTION_PCI_BURST_OFF, &TSENG.ChipOptionFlags); + OFLG_SET(OPTION_W32_INTERLEAVE_ON, &TSENG.ChipOptionFlags); + OFLG_SET(OPTION_W32_INTERLEAVE_OFF, &TSENG.ChipOptionFlags); + OFLG_SET(OPTION_SLOW_DRAM, &TSENG.ChipOptionFlags); + OFLG_SET(OPTION_FAST_DRAM, &TSENG.ChipOptionFlags); + } +/* + * because of some problems with W32 cards, SLOW_DRAM is _always_ enabled + * for those cards + */ + if (pTseng->ChipType <= TYPE_ET4000W32) { + ErrorF("%s %s: option \"slow_dram\" is enabled by default on this card.\n", + XCONFIG_PROBED, vga256InfoRec.name); + OFLG_SET(OPTION_SLOW_DRAM, &vga256InfoRec.options); + } + + ... + + vga256InfoRec.bankedMono = TRUE; + + ... + + + return (TRUE); +} + +#endif diff --git a/src/tseng_inline.h b/src/tseng_inline.h new file mode 100644 index 0000000..bc2ba77 --- /dev/null +++ b/src/tseng_inline.h @@ -0,0 +1,228 @@ +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/tseng_inline.h,v 1.8 2000/08/08 08:58:07 eich Exp $ */ + +#include "tseng.h" + +/* + * Some commonly used inline functions and utility functions. + */ + +static __inline__ int +COLOR_REPLICATE_DWORD(TsengPtr pTseng, int color) +{ + switch (pTseng->Bytesperpixel) { + case 1: + color &= 0xFF; + color = (color << 8) | color; + color = (color << 16) | color; + break; + case 2: + color &= 0xFFFF; + color = (color << 16) | color; + break; + } + return color; +} + +/* + * Optimizing note: increasing the wrap size for fixed-color source/pattern + * tiles from 4x1 (as below) to anything bigger doesn't seem to affect + * performance (it might have been better for larger wraps, but it isn't). + */ + +static __inline__ void +SET_FG_COLOR(TsengPtr pTseng, int color) +{ + ACL_SOURCE_ADDRESS(pTseng->AccelColorBufferOffset + pTseng->tsengFg); + ACL_SOURCE_Y_OFFSET(3); + color = COLOR_REPLICATE_DWORD(pTseng, color); + MMIO_OUT32(pTseng->scratchMemBase, pTseng->tsengFg, color); + + if (Is_W32p || Is_ET6K) { + ACL_SOURCE_WRAP(0x02); + } else { + MMIO_OUT32(pTseng->scratchMemBase,pTseng->tsengFg + 4, color); + ACL_SOURCE_WRAP(0x12); + } +} + +static __inline__ void +SET_BG_COLOR(TsengPtr pTseng, int color) +{ + ACL_PATTERN_ADDRESS(pTseng->AccelColorBufferOffset + pTseng->tsengPat); + ACL_PATTERN_Y_OFFSET(3); + color = COLOR_REPLICATE_DWORD(pTseng, color); + MMIO_OUT32(pTseng->scratchMemBase,pTseng->tsengPat, color); + if (Is_W32p || Is_ET6K) { + ACL_PATTERN_WRAP(0x02); + } else { + MMIO_OUT32(pTseng->scratchMemBase,pTseng->tsengPat + 4, color); + ACL_PATTERN_WRAP(0x12); + } +} + +/* + * this does the same as SET_FG_COLOR and SET_BG_COLOR together, but is + * faster, because it allows the PCI chipset to chain the requests into a + * burst sequence. The order of the commands is partly linear. + * So far for the theory... + */ +static __inline__ void +SET_FG_BG_COLOR(TsengPtr pTseng, int fgcolor, int bgcolor) +{ + ACL_PATTERN_ADDRESS(pTseng->AccelColorBufferOffset + pTseng->tsengPat); + ACL_SOURCE_ADDRESS(pTseng->AccelColorBufferOffset + pTseng->tsengFg); + ACL_PATTERN_Y_OFFSET32(0x00030003); + fgcolor = COLOR_REPLICATE_DWORD(pTseng, fgcolor); + bgcolor = COLOR_REPLICATE_DWORD(pTseng, bgcolor); + MMIO_OUT32(pTseng->scratchMemBase,pTseng->tsengFg, fgcolor); + MMIO_OUT32(pTseng->scratchMemBase,pTseng->tsengPat, bgcolor); + if (Is_W32p || Is_ET6K) { + ACL_PATTERN_WRAP32(0x00020002); + } else { + MMIO_OUT32(pTseng->scratchMemBase,pTseng->tsengFg + 4, fgcolor); + MMIO_OUT32(pTseng->scratchMemBase,pTseng->tsengPat + 4, bgcolor); + ACL_PATTERN_WRAP32(0x00120012); + } +} + +/* + * Real 32-bit multiplications are horribly slow compared to 16-bit (on i386). + */ +#ifdef NO_OPTIMIZE +static __inline__ int +MULBPP(TsengPtr pTseng, int x) +{ + return (x * pTseng->Bytesperpixel); +} +#else +static __inline__ int +MULBPP(TsengPtr pTseng, int x) +{ + int result = x << pTseng->powerPerPixel; + + if (pTseng->Bytesperpixel != 3) + return result; + else + return result + x; +} +#endif + +static __inline__ int +CALC_XY(TsengPtr pTseng, int x, int y) +{ + int new_x, xy; + + if ((pTseng->old_y == y) && (pTseng->old_x == x)) + return -1; + + if (Is_W32p) + new_x = MULBPP(pTseng, x - 1); + else + new_x = MULBPP(pTseng, x) - 1; + xy = ((y - 1) << 16) + new_x; + pTseng->old_x = x; + pTseng->old_y = y; + return xy; +} + +/* generic SET_XY */ +static __inline__ void +SET_XY(TsengPtr pTseng, int x, int y) +{ + int new_x; + + if (Is_W32p) + new_x = MULBPP(pTseng, x - 1); + else + new_x = MULBPP(pTseng, x) - 1; + ACL_XY_COUNT(((y - 1) << 16) + new_x); + pTseng->old_x = x; + pTseng->old_y = y; +} + +static __inline__ void +SET_X_YRAW(TsengPtr pTseng, int x, int y) +{ + int new_x; + + if (Is_W32p) + new_x = MULBPP(pTseng, x - 1); + else + new_x = MULBPP(pTseng, x) - 1; + ACL_XY_COUNT((y << 16) + new_x); + pTseng->old_x = x; + pTseng->old_y = y - 1; /* old_y is invalid (raw transfer) */ +} + +/* + * This is plain and simple "benchmark rigging". + * (no real application does lots of subsequent same-size blits) + * + * The effect of this is amazingly good on e.g large blits: 400x400 + * rectangle fill in 24 and 32 bpp on ET6000 jumps from 276 MB/sec to up to + * 490 MB/sec... But not always. There must be a good reason why this gives + * such a boost, but I don't know it. + */ + +static __inline__ void +SET_XY_4(TsengPtr pTseng, int x, int y) +{ + int new_xy; + + if ((pTseng->old_y != y) || (pTseng->old_x != x)) { + new_xy = ((y - 1) << 16) + MULBPP(pTseng, x - 1); + ACL_XY_COUNT(new_xy); + pTseng->old_x = x; + pTseng->old_y = y; + } +} + +static __inline__ void +SET_XY_6(TsengPtr pTseng, int x, int y) +{ + int new_xy; /* using this intermediate variable is faster */ + + if ((pTseng->old_y != y) || (pTseng->old_x != x)) { + new_xy = ((y - 1) << 16) + MULBPP(pTseng, x) - 1; + ACL_XY_COUNT(new_xy); + pTseng->old_x = x; + pTseng->old_y = y; + } +} + +/* generic SET_XY_RAW */ +static __inline__ void +SET_XY_RAW(TsengPtr pTseng,int x, int y) +{ + ACL_XY_COUNT((y << 16) + x); + pTseng->old_x = pTseng->old_y = -1; /* invalidate old_x/old_y (raw transfers) */ +} + +static __inline__ void +PINGPONG(TsengPtr pTseng) +{ + if (pTseng->tsengFg == 0) { + pTseng->tsengFg = 8; + pTseng->tsengBg = 24; + pTseng->tsengPat = 40; + } else { + pTseng->tsengFg = 0; + pTseng->tsengBg = 16; + pTseng->tsengPat = 32; + } +} + +/* + * This is called in each ACL function just before the first ACL register is +x * written to. It waits for the accelerator to finish on cards that don't + * support hardware-wait-state locking, and waits for a free queue entry on + * others, if hardware-wait-states are not enabled. + */ +static __inline__ void +wait_acl_queue(TsengPtr pTseng) +{ + if (pTseng->UsePCIRetry) + WAIT_QUEUE; + if (pTseng->need_wait_acl) + WAIT_ACL; +} diff --git a/src/tseng_ramdac.c b/src/tseng_ramdac.c new file mode 100644 index 0000000..ed4e667 --- /dev/null +++ b/src/tseng_ramdac.c @@ -0,0 +1,667 @@ +/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/tseng/tseng_ramdac.c,v 1.26 2001/10/28 03:33:53 tsi Exp $ */ + + + + + +/* + * + * Copyright 1993-1997 The XFree86 Project, Inc. + * + */ + +/* + * tseng_ramdac.c. + * + * Much of this code was taken from the XF86_W32 (3.2) server [kmg] + */ + +#include "tseng.h" + +SymTabRec TsengDacTable[] = +{ + {NORMAL_DAC, "normal"}, + {ATT20C47xA_DAC, "att20c47xa"}, + {Sierra1502X_DAC, "sc1502x"}, + {ATT20C497_DAC, "att20c497"}, + {ATT20C490_DAC, "att20c490"}, + {ATT20C493_DAC, "att20c493"}, + {ATT20C491_DAC, "att20c491"}, + {ATT20C492_DAC, "att20c492"}, + {ICS5341_DAC, "ics5341"}, + {ICS5301_DAC, "ics5301"}, + {STG1700_DAC, "stg1700"}, + {STG1702_DAC, "stg1702"}, + {STG1703_DAC, "stg1703"}, + {ET6000_DAC, "et6000"}, + {CH8398_DAC, "ch8398"}, + {MUSIC4910_DAC, "music4910"}, + {UNKNOWN_DAC, "unknown"}, +}; + +/*** private data ***/ + +#define RAMDAC_RMR 0x3c6 +#define RAMDAC_READ 0x3c7 +#define RAMDAC_WRITE 0x3c8 +#define RAMDAC_RAM 0x3c9 + +static unsigned char white_cmap[] = +{0xff, 0xff, 0xff}; + + +void +tseng_dactopel(void) +{ + outb(0x3C8, 0); + return; +} + +unsigned char +tseng_dactocomm(void) +{ + (void)inb(0x3C6); + (void)inb(0x3C6); + (void)inb(0x3C6); + return (inb(0x3C6)); +} + +unsigned char +tseng_getdaccomm(void) +{ + unsigned char ret; + + tseng_dactopel(); + (void)tseng_dactocomm(); + ret = inb(0x3C6); + tseng_dactopel(); + return (ret); +} + +void +tseng_setdaccomm(unsigned char comm) +{ + tseng_dactopel(); + (void)tseng_dactocomm(); + outb(0x3C6, comm); + tseng_dactopel(); + return; +} + +static Bool +ProbeSTG1703(TsengPtr pTseng, Bool quiet) +{ + unsigned char cid, did, daccomm, readmask; + Bool Found = FALSE; + + readmask = inb(RAMDAC_RMR); + tseng_dactopel(); + daccomm = tseng_getdaccomm(); + tseng_setdaccomm(daccomm | 0x10); + tseng_dactocomm(); + inb(0x3C6); + outb(RAMDAC_RMR, 0x00); + outb(RAMDAC_RMR, 0x00); + cid = inb(RAMDAC_RMR); /* company ID */ + did = inb(RAMDAC_RMR); /* device ID */ + tseng_dactopel(); + outb(RAMDAC_RMR, readmask); + tseng_setdaccomm(daccomm); + + if (cid == 0x44) { /* STG170x RAMDAC found */ + Found = TRUE; + switch (did) { + case 0x02: + pTseng->DacInfo.DacType = STG1702_DAC; + break; + case 0x03: + pTseng->DacInfo.DacType = STG1703_DAC; + break; + case 0x00: + default: + pTseng->DacInfo.DacType = STG1700_DAC; + /* treat an unknown STG170x as a 1700 */ + } + } + return (Found); +} + +static Bool +ProbeGenDAC(TsengPtr pTseng, int scrnIndex, Bool quiet) +{ + /* probe for ICS GENDAC (ICS5341) */ + /* + * GENDAC and SDAC have two fixed read only PLL clocks + * CLK0 f0: 25.255MHz M-byte 0x28 N-byte 0x61 + * CLK0 f1: 28.311MHz M-byte 0x3d N-byte 0x62 + * which can be used to detect GENDAC and SDAC since there is no chip-id + * for the GENDAC. + * + * code was taken from S3 XFree86 driver. + * NOTE: for the GENDAC on a ET4000W32p, reading PLL values + * for CLK0 f0 and f1 always returns 0x7f (but is documented "read only") + * In fact, all "read only" registers return 0x7f + */ + + unsigned char saveCR31, savelut[6]; + int i; + long clock01, clock23; + Bool found = FALSE; + unsigned char dbyte = 0; + int mclk = 0; + int iobase = VGAHW_GET_IOBASE(); + + outb(iobase + 4, 0x31); + saveCR31 = inb(iobase + 5); + + outb(iobase + 5, saveCR31 & ~0x40); + + outb(RAMDAC_READ, 0); + for (i = 0; i < 2 * 3; i++) /* save first two LUT entries */ + savelut[i] = inb(RAMDAC_RAM); + outb(RAMDAC_WRITE, 0); + for (i = 0; i < 2 * 3; i++) /* set first two LUT entries to zero */ + outb(RAMDAC_RAM, 0); + + outb(iobase + 4, 0x31); + outb(iobase + 5, saveCR31 | 0x40); + + outb(RAMDAC_READ, 0); + for (i = clock01 = 0; i < 4; i++) + clock01 = (clock01 << 8) | (inb(RAMDAC_RAM) & 0xff); + for (i = clock23 = 0; i < 4; i++) + clock23 = (clock23 << 8) | (inb(RAMDAC_RAM) & 0xff); + + /* get MClk value */ + outb(RAMDAC_READ, 0x0a); + mclk = (inb(RAMDAC_RAM) + 2) * 14318; + dbyte = inb(RAMDAC_RAM); + mclk /= ((dbyte & 0x1f) + 2) * (1 << ((dbyte >> 5) & 0x03)); + pTseng->MClkInfo.MemClk = mclk; + + outb(iobase + 4, 0x31); + outb(iobase + 5, saveCR31 & ~0x40); + + outb(RAMDAC_WRITE, 0); + for (i = 0; i < 2 * 3; i++) /* restore first two LUT entries */ + outb(RAMDAC_RAM, savelut[i]); + + outb(iobase + 4, 0x31); + outb(iobase + 5, saveCR31); + + if (clock01 == 0x28613d62 || + (clock01 == 0x7f7f7f7f && clock23 != 0x7f7f7f7f)) { + found = TRUE; + + tseng_dactopel(); + inb(RAMDAC_RMR); + inb(RAMDAC_RMR); + inb(RAMDAC_RMR); + + dbyte = inb(RAMDAC_RMR); + /* the fourth read will show the GenDAC/SDAC chip ID and revision */ + switch (dbyte & 0xf0) { + case 0xb0: + if (!quiet) { + xf86DrvMsg(scrnIndex, X_PROBED, "Ramdac: ICS 5341 GenDAC and programmable clock (MClk = %d MHz)\n", + mclk/1000); + } + pTseng->DacInfo.DacType = ICS5341_DAC; + break; + case 0xf0: + if (!quiet) { + xf86DrvMsg(scrnIndex, X_PROBED, "Ramdac: ICS 5301 GenDAC and programmable clock (MClk = %d MHz)\n", + mclk/1000); + } + pTseng->DacInfo.DacType = ICS5301_DAC; + break; + default: + if (!quiet) { + xf86DrvMsg(scrnIndex, X_PROBED, "Ramdac: unkown GenDAC and programmable clock (ID code = 0x%02x). Please report. (we'll treat it as a standard ICS5301 for now).\n", + dbyte); + } + pTseng->DacInfo.DacType = ICS5301_DAC; + } + tseng_dactopel(); + } + return found; +} + +/* probe for RAMDAC using the chip-ID method */ +static Bool +ProbeRamdacID(TsengPtr pTseng, Bool quiet) +{ + unsigned char cid; + Bool Found = FALSE; + + tseng_dactopel(); + cid = inb(RAMDAC_RMR); + cid = inb(RAMDAC_RMR); + cid = inb(RAMDAC_RMR); + cid = inb(RAMDAC_RMR); /* this returns chip ID */ + switch (cid) { + case 0xc0: + Found = TRUE; + pTseng->DacInfo.DacType = CH8398_DAC; + break; + case 0x82: + Found = TRUE; + pTseng->DacInfo.DacType = MUSIC4910_DAC; + break; + default: + Found = FALSE; + } + tseng_dactopel(); + + return Found; +} + +/* + * For a description of the following, see AT&T's data sheet for ATT20C490/491 + * and ATT20C492/493--GGL + */ + +static void +write_cr(int cr) +{ + inb(RAMDAC_WRITE); + xf86IODelay(); + inb(RAMDAC_RMR); + xf86IODelay(); + inb(RAMDAC_RMR); + xf86IODelay(); + inb(RAMDAC_RMR); + xf86IODelay(); + inb(RAMDAC_RMR); + xf86IODelay(); + outb(RAMDAC_RMR, cr); + xf86IODelay(); + inb(RAMDAC_WRITE); + xf86IODelay(); +} + +static int +read_cr(void) +{ + unsigned int cr; + + inb(RAMDAC_WRITE); + xf86IODelay(); + inb(RAMDAC_RMR); + xf86IODelay(); + inb(RAMDAC_RMR); + xf86IODelay(); + inb(RAMDAC_RMR); + xf86IODelay(); + inb(RAMDAC_RMR); + xf86IODelay(); + cr = inb(RAMDAC_RMR); + xf86IODelay(); + inb(RAMDAC_WRITE); + return cr; +} + +static void +write_color(int entry, unsigned char *cmap) +{ + outb(RAMDAC_WRITE, entry); + xf86IODelay(); + outb(RAMDAC_RAM, cmap[0]); + xf86IODelay(); + outb(RAMDAC_RAM, cmap[1]); + xf86IODelay(); + outb(RAMDAC_RAM, cmap[2]); + xf86IODelay(); +} + +static void +read_color(int entry, unsigned char *cmap) +{ + outb(RAMDAC_READ, entry); + xf86IODelay(); + cmap[0] = inb(RAMDAC_RAM); + xf86IODelay(); + cmap[1] = inb(RAMDAC_RAM); + xf86IODelay(); + cmap[2] = inb(RAMDAC_RAM); + xf86IODelay(); +} + +Bool +Check_Tseng_Ramdac(ScrnInfoPtr pScrn) +{ + unsigned char cmap[3], save_cmap[3]; + BOOL cr_saved; + int mclk; + int temp; + int dbyte; + TsengPtr pTseng = TsengPTR(pScrn); + rgb zeros = {0, 0, 0}; + + PDEBUG(" Check_Tseng_Ramdac\n"); + + pTseng->dac.rmr = inb(RAMDAC_RMR); + pTseng->dac.saved_cr = read_cr(); + cr_saved = TRUE; + + /* first see if ramdac type was given in XF86Config. If so, assume that is + * correct, and don't probe for it. + */ + if (pScrn->ramdac) { + pTseng->DacInfo.DacType = xf86StringToToken(TsengDacTable, pScrn->ramdac); + if (pTseng->DacInfo.DacType < 0) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unknown RAMDAC type \"%s\" specified\n", pScrn->ramdac); + return FALSE; + } + } else { /* autoprobe for the RAMDAC */ + if (Is_ET6K) { + pTseng->DacInfo.DacType = ET6000_DAC; + temp = inb(pTseng->IOAddress + 0x67); + outb(pTseng->IOAddress + 0x67, 10); + mclk = (inb(pTseng->IOAddress + 0x69) + 2) * 14318; + dbyte = inb(pTseng->IOAddress + 0x69); + mclk /= ((dbyte & 0x1f) + 2) * (1 << ((dbyte >> 5) & 0x03)); + pTseng->MClkInfo.MemClk = mclk; + } else if (ProbeGenDAC(pTseng, pScrn->scrnIndex, FALSE)) { + /* It is. Nothing to do here */ + } else if (ProbeSTG1703(pTseng, FALSE)) { + /* it's a STG170x */ + } else if (ProbeRamdacID(pTseng, FALSE)) { + /* found one using RAMDAC ID code */ + } else + /* if none of the above: start probing for other DACs */ + { + outb(RAMDAC_RMR, 0xff); + xf86IODelay(); + inb(RAMDAC_RMR); + xf86IODelay(); + inb(RAMDAC_RMR); + xf86IODelay(); + inb(RAMDAC_RMR); + xf86IODelay(); + inb(RAMDAC_RMR); + xf86IODelay(); + outb(RAMDAC_RMR, 0x1c); + xf86IODelay(); + + if (inb(RAMDAC_RMR) != 0xff) { + cr_saved = FALSE; + pTseng->DacInfo.DacType = ATT20C47xA_DAC; + goto dac_found; + } + write_cr(0xe0); + if ((read_cr() >> 5) != 0x7) { + pTseng->DacInfo.DacType = ATT20C497_DAC; + goto dac_found; + } + write_cr(0x60); + if ((read_cr() >> 5) == 0) { + write_cr(0x2); + if ((read_cr() & 0x2) != 0) + pTseng->DacInfo.DacType = ATT20C490_DAC; + else + pTseng->DacInfo.DacType = ATT20C493_DAC; + } else { + write_cr(0x2); + outb(RAMDAC_RMR, 0xff); + read_color(0xff, save_cmap); + + write_color(0xff, white_cmap); + read_color(0xff, cmap); + + if (cmap[0] == 0xff && cmap[1] == 0xff && cmap[2] == 0xff) + pTseng->DacInfo.DacType = ATT20C491_DAC; + else + pTseng->DacInfo.DacType = ATT20C492_DAC; + + write_color(0xff, save_cmap); + } + } + } + + dac_found: + /* defaults: 8-bit wide DAC port, 6-bit color lookup-tables */ + pTseng->DacInfo.RamdacShift = 10; + pTseng->DacInfo.RamdacMask = 0x3f; + pTseng->DacInfo.Dac8Bit = FALSE; + pTseng->DacInfo.DacPort16 = FALSE; + pTseng->DacInfo.NotAttCompat = FALSE; /* default: treat as ATT compatible DAC */ + pTseng->DacInfo.rgb24packed = zeros; + pScrn->progClock = FALSE; + pTseng->ClockChip = -1; + pTseng->MClkInfo.Programmable = FALSE; + + /* now override defaults with appropriate values for each RAMDAC */ + switch (pTseng->DacInfo.DacType) { + case ATT20C490_DAC: + case ATT20C491_DAC: + pTseng->DacInfo.RamdacShift = 8; + pTseng->DacInfo.RamdacMask = 0xff; + pTseng->DacInfo.Dac8Bit = TRUE; + break; + case UNKNOWN_DAC: + case Sierra1502X_DAC: + pTseng->DacInfo.NotAttCompat = TRUE; /* avoids treatment as ATT compatible DAC */ + break; + case ET6000_DAC: + pScrn->progClock = TRUE; + pTseng->ClockChip = CLOCKCHIP_ET6000; + pTseng->DacInfo.NotAttCompat = TRUE; /* avoids treatment as ATT compatible DAC */ + pTseng->MClkInfo.Programmable = TRUE; + pTseng->MClkInfo.min = 80000; + pTseng->MClkInfo.max = 110000; + break; + case ICS5341_DAC: + pScrn->progClock = TRUE; + pTseng->ClockChip = CLOCKCHIP_ICS5341; + pTseng->MClkInfo.Programmable = TRUE; + pTseng->MClkInfo.min = 40000; + pTseng->MClkInfo.max = 60000; + pTseng->DacInfo.DacPort16 = TRUE; + pTseng->DacInfo.rgb24packed.red = 0xff; + pTseng->DacInfo.rgb24packed.green = 0xff0000; + pTseng->DacInfo.rgb24packed.blue = 0xff00; + break; + case ICS5301_DAC: + pScrn->progClock = TRUE; + pTseng->ClockChip = CLOCKCHIP_ICS5301; + break; + case STG1702_DAC: + case STG1700_DAC: + pTseng->DacInfo.DacPort16 = TRUE; + break; + case STG1703_DAC: + pScrn->progClock = TRUE; + pTseng->ClockChip = CLOCKCHIP_STG1703; + pTseng->DacInfo.DacPort16 = TRUE; + break; + case CH8398_DAC: + pScrn->progClock = TRUE; + pTseng->ClockChip = CLOCKCHIP_CH8398; + pTseng->DacInfo.DacPort16 = TRUE; + break; + default: + /* defaults already set */ + ; + } + + xf86DrvMsg(pScrn->scrnIndex, (pScrn->ramdac) ? X_CONFIG : X_PROBED, "Ramdac: \"%s\"\n", + xf86TokenToString(TsengDacTable, pTseng->DacInfo.DacType)); + + if (cr_saved && pTseng->DacInfo.RamdacShift == 10) + write_cr(pTseng->dac.saved_cr); + outb(RAMDAC_RMR, 0xff); + + return TRUE; +} + +/* + * The following arrays hold command register values for all possible + * modes of the 16-bit DACs used on ET4000W32p cards (bpp/pixel_bus_width): + * + * { 8bpp/8, 15bpp/8, 16bpp/8, 24bpp/8, 32bpp/8, + * 8bpp/16, 15bpp/16, 16bpp/16, 24bpp/16, 32bpp/16 + * } + * + * "0xFF" is used as a "not-supported" flag. Assuming no RAMDAC uses this + * value for some real configuration... + */ +static unsigned char CMD_GENDAC[] = +{0x00, 0x20, 0x60, 0x40, 0xFF, + 0x10, 0x30, 0x50, 0x90, 0x70}; + +static unsigned char CMD_STG170x[] = +{0x00, 0x08, 0xFF, 0xFF, 0xFF, + 0x05, 0x02, 0x03, 0x09, 0x04}; + +static unsigned char CMD_CH8398[] = +{0x04, 0xC4, 0x64, 0x74, 0xFF, + 0x24, 0x14, 0x34, 0xB4, 0xFF}; + +static unsigned char CMD_ATT49x[] = +{0x00, 0xa0, 0xc0, 0xe0, 0xe0, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +#if 0 +static unsigned char CMD_SC15025[] = +{0x00, 0xa0, 0xe0, 0x60, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +#endif + +static unsigned char CMD_MU4910[] = +{0x1C, 0xBC, 0xDC, 0xFC, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +/* + * This sets up the RAMDAC registers for the correct BPP and pixmux values. + * (also set VGA controller registers for pixmux and BPP) + */ +void +tseng_set_ramdac_bpp(ScrnInfoPtr pScrn, DisplayModePtr mode) +{ + Bool rgb555; + Bool dac16bit; /* use DAC in 16-bit mode if set (W32p only) */ + unsigned char *cmd_array = NULL; + unsigned char *cmd_dest = NULL; + int index; + TsengPtr pTseng = TsengPTR(pScrn); + + rgb555 = (pScrn->weight.red == 5 && pScrn->weight.green == 5 + && pScrn->weight.blue == 5); /* rgb565 otherwise */ + + /* This is not the good way to find out if we're in 8- or 16-bit RAMDAC + * mode It should rather be passed on from the TsengValidMode() code. + * Right now it'd better agree with what TsengValidMode() proposed. FIXME + */ + dac16bit = (mode->PrivFlags == TSENG_MODE_DACBUS16) || + (mode->PrivFlags == TSENG_MODE_PIXMUX); + + pTseng->ModeReg.ExtATC &= 0xCF; /* ATC index 0x16 -- bits-per-PCLK */ + if (Is_ET6K) + pTseng->ModeReg.ExtATC |= (pTseng->Bytesperpixel - 1) << 4; + else if (dac16bit) + pTseng->ModeReg.ExtATC |= 0x20; + + switch (pTseng->DacInfo.DacType) { + case ATT20C490_DAC: + case ATT20C491_DAC: + case ATT20C492_DAC: + case ATT20C493_DAC: + cmd_array = CMD_ATT49x; + cmd_dest = &(pTseng->ModeReg.ATTdac_cmd); + break; + case STG1700_DAC: + case STG1702_DAC: + case STG1703_DAC: + pTseng->ModeReg.pll.cmd_reg &= 0x04; /* keep 7.5 IRE setup setting */ + pTseng->ModeReg.pll.cmd_reg |= 0x18; /* enable ext regs and pixel modes */ + switch (pTseng->Bytesperpixel) { + case 2: + if (rgb555) + pTseng->ModeReg.pll.cmd_reg |= 0xA0; + else + pTseng->ModeReg.pll.cmd_reg |= 0xC0; + break; + case 3: + case 4: + pTseng->ModeReg.pll.cmd_reg |= 0xE0; + break; + } + cmd_array = CMD_STG170x; + cmd_dest = &(pTseng->ModeReg.pll.ctrl); + /* set PLL (input) range */ + if (mode->SynthClock <= 16000) + pTseng->ModeReg.pll.timingctrl = 0; + else if (mode->SynthClock <= 32000) + pTseng->ModeReg.pll.timingctrl = 1; + else if (mode->SynthClock <= 67500) + pTseng->ModeReg.pll.timingctrl = 2; + else + pTseng->ModeReg.pll.timingctrl = 3; + break; + case ICS5341_DAC: + case ICS5301_DAC: + cmd_array = CMD_GENDAC; + pTseng->ModeReg.pll.ctrl = 0; + cmd_dest = &(pTseng->ModeReg.pll.cmd_reg); + break; + case CH8398_DAC: + cmd_array = CMD_CH8398; + cmd_dest = &(pTseng->ModeReg.pll.cmd_reg); + break; + case ET6000_DAC: + if (pScrn->bitsPerPixel == 16) { + if (rgb555) + pTseng->ModeReg.ExtET6K[0x58] &= ~0x02; /* 5-5-5 RGB mode */ + else + pTseng->ModeReg.ExtET6K[0x58] |= 0x02; /* 5-6-5 RGB mode */ + } + break; + case MUSIC4910_DAC: + cmd_array = CMD_MU4910; + cmd_dest = &(pTseng->ModeReg.ATTdac_cmd); + break; + default: + break; + } + + if (cmd_array != NULL) { + switch (pTseng->Bytesperpixel) { + default: + case 1: + index = 0; + break; + case 2: + index = rgb555 ? 1 : 2; + break; + case 3: + index = 3; + break; + case 4: + index = 4; + break; + } + if (dac16bit) + index += 5; + if (cmd_array[index] != 0xFF) { + if (cmd_dest != NULL) { + *cmd_dest = cmd_array[index]; + } else + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, " cmd_dest = NULL -- please report\n"); + } else { + pTseng->ModeReg.pll.cmd_reg = 0; + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, " %dbpp not supported in %d-bit DAC mode on this RAMDAC -- Please report.\n", + pScrn->bitsPerPixel, dac16bit ? 16 : 8); + } + } +#ifdef FIXME /* still needed? */ + if (mode->PrivFlags == TSENG_MODE_PIXMUX) { + VGAHWPTR(pScrn)->ModeReg.CRTC[0x17] &= 0xFB; + + /* to avoid blurred vertical line during flyback, disable H-blanking + * (better solution needed !!!) + */ + VGAHWPTR(pScrn)->ModeReg.CRTC[0x02] = 0xff; + } +#endif +} |