/* $XConsortium: mach8init.c,v 1.3 95/01/06 20:57:09 kaleb Exp $ */
/* $XFree86: xc/programs/Xserver/hw/xfree86/accel/mach8/mach8init.c,v 3.4 1995/01/28 17:00:51 dawes Exp $ */
/*
 * Written by Jake Richter
 * Copyright (c) 1989, 1990 Panacea Inc., Londonderry, NH - All Rights Reserved
 *
 * This code may be freely incorporated in any program without royalty, as
 * long as the copyright notice stays intact.
 *
 * Additions by Kevin E. Martin (martin@cs.unc.edu)
 *
 * KEVIN E. MARTIN, RICKARD E. FAITH, SCOTT LAIRD, AND TIAGO GONS DISCLAIM
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 * KEVIN E. MARTIN, RICKARD E. FAITH, SCOTT LAIRD, OR TIAGO GONS 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.
 *
 * Modified for the Mach-8 by Rickard E. Faith (faith@cs.unc.edu)
 * Further modifications by Scott Laird (lair@kimbark.uchicago.edu)
 * and Tiago Gons (tiago@comosjn.hobby.nl)
 */


#include "X.h"
#include "input.h"
#include "mach8.h"
#include "regmach8.h"
#include "xf86.h"
#include "xf86_OSlib.h"

#define XCONFIG_FLAGS_ONLY
#include "xf86_Config.h"

static short numPlanes = -1;
static short resolution = -1;
static short old_DAC_MASK = -1;
static LUTENTRY oldlut[256];
static short LUTInited = -1;


/* mach8calcvmode--
 *      Calculate Mach8 register values from display specifications
 *      from the XF86Config file.
 */

void
mach8calcvmode(vmdef, pMode)
     struct mach8vmodedef *vmdef;
     DisplayModePtr pMode;
{
    vmdef->dc  = DISPEN_ENAB | MEMCFG_4 | ODDBNKENAB;

    vmdef->clk = pMode->Clock;

    vmdef->hd  = (pMode->CrtcHDisplay >> 3) - 1;
    vmdef->ht  = (pMode->CrtcHTotal >> 3) - 1;
    /*vmdef->hss = (pMode->CrtcHSyncStart >> 3) - 1;*/
    vmdef->hss = (pMode->CrtcHSyncStart - 1) >> 3;
    vmdef->hsw = (pMode->CrtcHSyncEnd - pMode->CrtcHSyncStart) >> 3;
    if(vmdef->hsw > 0x1f)
    {
        ErrorF("%s %s: Horizontal Sync width (%d) in mode \"%s\"\n",
               XCONFIG_PROBED, mach8InfoRec.name, vmdef->hsw, pMode->name);
	ErrorF("\tshortened to 248 pixels\n");
    }
    vmdef->hsw &= 0x1F;

    vmdef->vd  = ((((pMode->CrtcVDisplay-1)<<1)&0xFFF8) | 
                 (((pMode->CrtcVDisplay-1))&0x3)) |
                 ((((pMode->CrtcVDisplay-1)&0x80)>>5));

    vmdef->vt  = ((((pMode->CrtcVTotal-1)<<1)&0xFFF8) | 
                 (((pMode->CrtcVTotal-1)&0x3)) |
                 (((pMode->CrtcVTotal-1)&0x80)>>5));

    vmdef->vss = ((((pMode->CrtcVSyncStart-1)<<1)&0xFFF8) | 
                 (((pMode->CrtcVSyncStart-1)&0x3)) |
                 (((pMode->CrtcVSyncStart-1)&0x80)>>5));

    vmdef->vsw = pMode->CrtcVSyncEnd - pMode->CrtcVSyncStart;
    if(vmdef->vsw > 0x1f)
    {
        ErrorF("%s %s: Vertical Sync width (%d) in mode \"%s\"\n",
               XCONFIG_PROBED, mach8InfoRec.name, vmdef->vsw, pMode->name);
	ErrorF("\tshortened to 31 lines\n");
    }
    vmdef->vsw &= 0x1F;

    if(pMode->Flags & V_NHSYNC)
        vmdef->hsw |= HSYNCPOL_NEG;

    if(pMode->Flags & V_NVSYNC)
        vmdef->vsw |= VSYNCPOL_NEG;

    if(pMode->Flags & V_INTERLACE)
        vmdef->dc |= INTERLACE;

    if(pMode->Flags & V_CSYNC)
        vmdef->clk |= COMPOSITE_SYNC;

/*
    ErrorF("Mach8calcvmode: (%x,%x,%x,%x),(%x,%x,%x,%x),(%x,%x)\n",
           vmdef->hd,vmdef->hss,vmdef->hsw,vmdef->ht,      
           vmdef->vd,vmdef->vss,vmdef->vsw,vmdef->vt,
           vmdef->dc,vmdef->clk);
*/
}


/*
 * mach8setcrtregs --
 *      Program the Mach8 for arbitrary resolutions.
 */

void
mach8setcrtregs(vmdef)
     struct mach8vmodedef *vmdef;
{
    /* TCG : disable crt and unlock shadow sets */
    outw(DISP_CNTL, DISPEN_DISAB | MEMCFG_4 | ODDBNKENAB);
    outw(SHADOW_SET, 1);
    outw(SHADOW_CTL, 0);
    outw(SHADOW_SET, 2);
    outw(SHADOW_CTL, 0);
    outw(SHADOW_SET, 0);

    /* set number of pixels per scan line in memory.  This is tied to
     * virtualX.
     */

    if( mach8InfoRec.videoRam > 512 ) {
	outw( CRT_PITCH, 1024 >> 3 );
	outw( GE_PITCH, 1024 >> 3 );
    }
    else {
	outw(CRT_PITCH, mach8InfoRec.virtualX >> 3);
	outw(GE_PITCH, mach8InfoRec.virtualX >> 3);
    }
    outw(H_TOTAL,vmdef->ht);
    outw(H_DISP,vmdef->hd);
    outw(H_SYNC_STRT,vmdef->hss);
    outw(H_SYNC_WID,vmdef->hsw);

    outw(V_TOTAL,vmdef->vt);
    outw(V_DISP,vmdef->vd);
    outw(V_SYNC_STRT,vmdef->vss);
    outw(V_SYNC_WID,vmdef->vsw);

    /* 
     * Make sure that the start of video memory is at the beginning of 
     * memory.  We use these for virtual display panning.
     */
    /* TCG : adjust frame after vt switch */
    if (mach8InfoRec.frameX0 != -1 && mach8InfoRec.frameY0 != -1)
	mach8Adjust(mach8InfoRec.frameX0, mach8InfoRec.frameY0);
    else {
	outw(GE_OFFSET_HI,0);
	outw(GE_OFFSET_LO,0);
	outw(CRT_OFFSET_HI,0);
	outw(CRT_OFFSET_LO,0);
    }

    if (OFLG_ISSET(OPTION_CSYNC, &mach8InfoRec.options))
	outw(CLOCK_SEL, ((vmdef->clk & 0x00ff) << 2) | 0xa01 | COMPOSITE_SYNC);
    else
	outw(CLOCK_SEL,
	 ((vmdef->clk & 0x00ff) << 2) | 0xa01 | (vmdef->clk & COMPOSITE_SYNC));

    outw(DISP_CNTL,vmdef->dc);
}


void
mach8CleanUp(void)
{
    int i;

    if (LUTInited != -1) {
	outb(DAC_W_INDEX, 0);
	for (i = 0; i < 256; i++) {
	    outb(DAC_DATA, oldlut[i].r);
	    outb(DAC_DATA, oldlut[i].g);
	    outb(DAC_DATA, oldlut[i].b);
	}
    }

    if (old_DAC_MASK != -1) {
	outb(DAC_MASK, old_DAC_MASK);
    }
    outw(ADVFUNC_CNTL, 6);

    xf86DisableIOPorts(mach8InfoRec.scrnIndex);
}

/*	mach8Init(res)
 *
 *    Initializes the Mach-8 into the requested resolution.  This takes
 *    a mach8vmodedef value generated by mach8calcvmode and sets up
 *    the card.
 */
void
mach8Init(vmdef)
     struct mach8vmodedef *vmdef;
{
    short i;

    /* Get board status information */
    i = inw(SUBSYS_STAT);

    /* Determine the pixel depth of the board.  Then, based on depth,
     * set the memory control register use the appropriate CAS (VRAM
     * Column Address Select) configuration.
     */
    if (i & _8PLANE) {
	numPlanes = 8;
	outw(MULTIFUNC_CNTL, MEM_CNTL | VRTCFG_4 | HORCFG_8);
    } else {
	numPlanes = 4;
	mach8CleanUp();
	FatalError("Video card only has 512Kb (1Mb required)!\n");
    }

    old_DAC_MASK = inb(DAC_MASK);
    outb(DAC_MASK, 0x00);

    mach8setcrtregs(vmdef);

    /* Now reenable the screen, but only the planes that actually exist.
     * Otherwise, you end up with bus noise on the display.
     */
    if (numPlanes == 8)
	outb(DAC_MASK, 0xff);
    else
	outb(DAC_MASK, 0x0f);

    resolution = MODE_CUSTOM;
}

/*	InitLUT()

	Loads the Look-Up Table with all black.
	Assumes 8-bit board is in use.  If 4 bit board, the only the first
	16 entries in LUT will be used.
*/
void InitLUT(void)
{
    short i, j;

    outb(DAC_R_INDEX, 0);
    for (i = 0; i < 256; i++) {
	oldlut[i].r = inb(DAC_DATA);
	oldlut[i].g = inb(DAC_DATA);
	oldlut[i].b = inb(DAC_DATA);
    }
    LUTInited = 1;

    outb(DAC_W_INDEX, 0);

    /* Load the first 16 LUT entries */
    for (i = 0; i < 16; i++) {
	outb(DAC_DATA, 0);
	outb(DAC_DATA, 0);
	outb(DAC_DATA, 0);
    }

    if (numPlanes == 8) {
	/* Load the remaining 240 LUT entries */
	for (i = 1; i < 16; i++) {
	    for (j = 0; j < 16; j++) {
		outb(DAC_DATA, 0);
		outb(DAC_DATA, 0);
		outb(DAC_DATA, 0);
	    }
	}
    }
}

/*	mach8InitEnvironment()

	Initializes the Mach-8's drawing environment and clears the display.
*/
void mach8InitEnvironment()
{
    /* Current mixes, src, foreground active */
    outw(FRGD_MIX, FSS_FRGDCOL | MIX_SRC);
    outw(BKGD_MIX, BSS_BKGDCOL | MIX_SRC);

    /* Clipping rectangle to full drawable space */
    outw(MULTIFUNC_CNTL, SCISSORS_T | 0x000);
    outw(MULTIFUNC_CNTL, SCISSORS_L | 0x000);
    if (resolution == MODE_640) {
	outw(MULTIFUNC_CNTL, SCISSORS_R | 0x3ff); /* Should these be */
	outw(MULTIFUNC_CNTL, SCISSORS_B | 0x3ff); /* different???    */
    } else {
	outw(MULTIFUNC_CNTL, SCISSORS_R | 0x3ff);
	outw(MULTIFUNC_CNTL, SCISSORS_B | 0x3ff);
    }

    /* Enable writes to all planes and reset color compare */
    outw(WRT_MASK, 0xffff);
    outw(MULTIFUNC_CNTL, PIX_CNTL | 0x0000);

/*#define DIRTY_STARTUP*/
#ifndef DIRTY_STARTUP
    if (serverGeneration == 1) /* 5-5-93 TCG : servergen.. is always 1 ? */
    {
        /* Clear the display.  Need to set the color, origin, and size.
         * Then draw.
         */
        WaitQueue(6);
        outw(FRGD_COLOR, 1);
        outw(CUR_X, 0);
        outw(CUR_Y, 0);
        outw(MAJ_AXIS_PCNT, 1023);
        outw(MULTIFUNC_CNTL, MIN_AXIS_PCNT | 1023);
        outw(CMD, CMD_RECT | INC_Y | INC_X | DRAW | PLANAR | WRTDATA);
    }
#endif /* ! DIRTY_STARTUP */

    WaitQueue(4);

    /* Reset current draw position */
    outw(CUR_X, 0);
    outw(CUR_Y, 0);

    /* Reset current colors, foreground is all on, background is 0. */
    outw(FRGD_COLOR, 0xffff);
    outw(BKGD_COLOR, 0x0000);

    /* Load the LUT */
    InitLUT();
}
