summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaleb Keithley <kaleb@freedesktop.org>2003-11-14 16:48:55 +0000
committerKaleb Keithley <kaleb@freedesktop.org>2003-11-14 16:48:55 +0000
commitf8465308152c64c6e05ebf814794aec1022046c5 (patch)
tree29ed6714ef5efd83b989cc4f5a9e3a239cb144ac
Initial revisionXORG-STABLE
-rw-r--r--man/tseng.man30
-rw-r--r--src/README264
-rw-r--r--src/tseng.h391
-rw-r--r--src/tseng_accel.c936
-rw-r--r--src/tseng_acl.c232
-rw-r--r--src/tseng_acl.h232
-rw-r--r--src/tseng_bank.c87
-rw-r--r--src/tseng_clock.c508
-rw-r--r--src/tseng_colexp.c543
-rw-r--r--src/tseng_cursor.c270
-rw-r--r--src/tseng_dga.c249
-rw-r--r--src/tseng_dpms.c249
-rw-r--r--src/tseng_driver.c3292
-rw-r--r--src/tseng_inline.h228
-rw-r--r--src/tseng_ramdac.c667
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
+}