/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */
/* THIS DRIVER DOES NOT WORK */



/* VGAlib version 1.2 - (c) 1993 Tommy Frandsen                    */
/*                                                                 */
/* This library is free software; you can redistribute it and/or   */
/* modify it without any restrictions. This library is distributed */
/* in the hope that it will be useful, but without any warranty.   */

/* Multi-chipset support Copyright (C) 1993 Harm Hanemaayer */

/* 
   Initial ATI Mach64 Driver. 
   August 1995. 
   Asad F. Hanif (w81h@unb.ca)
   Copyright (C) 1995 Asad F. Hanif (w81h@unb.ca)
 */

/*
   Note: This code is based on the material provided to me by
   the folks at ATI.  As well portions are based on
   the Mach64 XFree86 Server code.  

 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>

#include "vga.h"
#include "libvga.h"
#include "driver.h"

#include "mach64.h"

/* globals */
static int DUMPINDEX;
static char DUMPOUT[33];
static US ioBaseAddress, PciIOLocation;
static UB cTabSize, *RomLocation;
static UL *mreg;

static M64_ClockInfo ClockInfo;
static M64_MaxColorDepth MaxCDT1[50], MaxCDT2[50], FreqCDT1[50], FreqCDT2[50];
static M64_StartUpInfo StartupInfo;
/*static CRTC_Table *pcrtc; */

/* prototypes */
/* svgalib - driverspec functions */
static void nothing(void);
static int mach64_init(int, int, int);
static int mach64_test(void);
static int mach64_saveregs(UB *);
static void mach64_setregs(const UB *, int);
static void mach64_getmodeinfo(int, vga_modeinfo *);
static int mach64_modeavailable(int);
static int mach64_setmode(int, int);
static void mach64_setdisplaystart(int);
static void mach64_setlogicalwidth(int);
static void mach64_setpage(int);

/* misc. functions */
static UB *mach64_rom_base(void);
static UB *mach64_map_vbios(UB *);

/*static void mach64_unmap_vbios(UB *,UB *); */

static void mach64_get_alot_of_info(UB *);
static void mach64_report(void);

/* dac control and programming */
#define DAC_TYPE	StartupInfo.Dac_Type
#define DAC_SUBTYPE	StartupInfo.Dac_Subtype

static int mach64_dac_ati68860(US, US);
static void mach64_dac_program(US, US, US);
static void mach64_crtc_programming(US, US, US);

static void mach64_small_aperature(void);

/* regs to save */
static int sr_accel[13] =
{ioCRTC_GEN_CNTL,
 ioCRTC_H_SYNC_STRT_WID,
 ioCRTC_H_TOTAL_DISP,
 ioCRTC_V_SYNC_STRT_WID,
 ioCRTC_V_TOTAL_DISP,
 ioCRTC_OFF_PITCH,
 ioCLOCK_SEL_CNTL,
 ioOVR_CLR,
 ioOVR_WID_LEFT_RIGHT,
 ioOVR_WID_TOP_BOTTOM,
 ioMEM_CNTL,
 ioMEM_VGA_RP_SEL,
 ioMEM_VGA_WP_SEL};

/* Driverspecs function table (exported) */
DriverSpecs __svgalib_mach64_driverspecs =
{
    mach64_saveregs,
    mach64_setregs,
    (void (*)(void)) nothing,	/* unlock */
    (void (*)(void)) nothing,	/* lock */
    mach64_test,
    mach64_init,
    mach64_setpage,		/* __svgalib_setpage */
    (void (*)(int)) nothing,	/* __svgalib_setrdpage */
    (void (*)(int)) nothing,	/* __svgalib_setwrpage */
    mach64_setmode,
    mach64_modeavailable,
    mach64_setdisplaystart,
    mach64_setlogicalwidth,
    mach64_getmodeinfo,
    0,				/* bitblt */
    0,				/* imageblt */
    0,				/* fillblt */
    0,				/* hlinelistblt */
    0,				/* bltwait */
    0,				/* extset */
    0,
    0,				/* linear */
    NULL,                       /* Accelspecs */
    NULL,                       /* Emulation */
};

/* empty function */
static void nothing(void)
{
}

/***************************************************************************/

/* Initialize chipset */
static int mach64_init(int force, int par1, int par2)
{
    UB *atibios;

    if (__svgalib_driver_report)
	fprintf(stdout, "Using ATI Mach64 driver.\n");

    __svgalib_driverspecs = &__svgalib_mach64_driverspecs;

    /* Make sure we are operating at IO priveleges are level 3 */
    if (iopl(3) < 0) {
	fprintf(stdout, "iopl(3) failed in Mach64\n");
	exit(-1);
    }
    /* download  configuration data */
    atibios = mach64_map_vbios(RomLocation);
    mach64_get_alot_of_info(atibios);
/* 
   This seems to be a cause of segfaults.
   mach64_unmap_vbios(RomLocation,atibios);
 */
    mach64_report();

#ifdef BAILOUT
    fprintf(stdout, "\n*** WARNING ALPHA DRIVER ***\n" \
	    "*** If the above diagnostics look ok then press 'Y' to" \
	    "continue.\n\n");
    if (getchar() != 'Y') {
	fprintf(stdout, "\n*** Bailing out! ***\n");
	exit(-1);
    }
#endif
    return 1;
}

/* test for presence of mach64 */
static int mach64_test(void)
{
    UB *atibios;
    UB ati_sig1[] = "761295520";
    UB ati_sig2[] = "GXCX";
    UB ati_sig3[] = "MACH64";
    int ati_found, i;
    UL sv;
    US romtabptr, *romoff;

    /* Make sure we are operating at IO privlege level 3 */
    if (iopl(3) < 0) {
	fprintf(stdout, "iopl(3) failed in Mach64\n");
	exit(-1);
    }
    /* !!should loop through the relocatable bios areas - instead */
    /* !!should read the address cause it may be relocateable */
    RomLocation = mach64_rom_base();

    /* map the vga bios */
    atibios = mach64_map_vbios(RomLocation);

    /* scan for the signature */
    ati_found = 0;
    for (i = 0; i < 128; i++) {
	if (strncmp(ati_sig1, atibios + i, sizeof(ati_sig1) - 1) == 0) {
	    ati_found = 1;
	    break;
	}
    }

    /* scan for either GXCX or MACH64 */
    for (i = 0; i < 1024; i++) {
	if (strncmp(ati_sig2, atibios + i, sizeof(ati_sig2) - 1) == 0) {
	    ati_found++;
	    break;
	}
	if (strncmp(ati_sig3, atibios + i, sizeof(ati_sig3) - 1) == 0) {
	    ati_found++;
	    break;
	}
    }


    /* grab the address for io access - fixed and relocatable from bios 
       I can't tell if its VLB or PCI yet.  If the ioBaseAddress is not 
       0x2EC, 0x1CC, or 0x1C8 then its obviosly relocatable (we hope).
       If its relocatable then we pray its PCI. */

    romoff = (US *) atibios;
    romtabptr = romoff[0x48 >> 1];
    ioBaseAddress = romoff[(romtabptr >> 1) + 2];
    PciIOLocation = romoff[(romtabptr >> 1) + 13];
    switch (ioBaseAddress) {
    case 0x2ec:
	break;
    case 0x1cc:
    case 0x1c8:
    default:
	fprintf(stdout, "M64: Can't handle io address yet.\n");
	exit(-1);
    }
#ifdef DEBUG
    fprintf(stdout, "M64: IO Base Address: %x\n", ioBaseAddress);
#endif
    /* unmap the bios */
/*      This seems to be the cause of segfaults 
   mach64_unmap_vbios(RomLocation,atibios);
 */
    /* if its not even ati return a failure */
    if (ati_found < 2)
	return 0;

    /* do the scratch register io confirmation test */
    /* assume not mach64 */
    ati_found = 0;

    /* save scratch value */
    sv = inl(ioSCRATCH_REG0);

    /* test odd bits for readability */
    outl(ioSCRATCH_REG0, 0x55555555);
    if (inl(ioSCRATCH_REG0) == 0x55555555) {
	/* test even bits for readability */
	outl(ioSCRATCH_REG0, 0xaaaaaaaa);
	if (inl(ioSCRATCH_REG0) == 0xaaaaaaaa) {
	    ati_found = 1;
	}
    }
    /* restore */
    outl(ioSCRATCH_REG0, sv);

    return (ati_found) ? mach64_init(0, 0, 0) : ati_found;
}

/**************************************************************************/
/* locate rom locatation */
static UB *mach64_rom_base(void)
{
    US a;
    UB *b;

    a = inw(ioSCRATCH_REG1);
    a &= 0x7f;
    a += 0xc000;
    b = (UB *) (a * 0x10);

    return (b);
}

/* map the vga bios - pretty much lifted from ati.c */
static UB *mach64_map_vbios(UB * map_me)
{
    UB *vga_bios;

    /* allocate 32k for bios map */
    if ((vga_bios = valloc(M64_BIOS_SIZE)) == NULL) {
	fprintf(stdout, "SVGALIB valloc failure\n");
	exit(-1);
    }
    vga_bios = (UB *) mmap((caddr_t) vga_bios,
			   M64_BIOS_SIZE,
			   PROT_READ,
			   MAP_SHARED | MAP_FIXED,
			   __svgalib_mem_fd,
			   (off_t) map_me);

    if ((long) vga_bios < 0) {
	fprintf(stdout, "SVGALIB mmap failure\n");
	exit(-1);
    }
    return (UB *) vga_bios;
}

/*
   static void mach64_unmap_vbios(UB *unmap_me,UB *free_me)
   {
   if (munmap((caddr_t)unmap_me,M64_BIOS_SIZE)) {
   fprintf(stdout,"SVGALIB munmap failure\n");
   exit(-1);
   }
   free((void *)free_me);
   }
 */

/****************************************************************************/

/* collect information from rom and registers */
static void mach64_get_alot_of_info(UB * mapped_area)
{
    UB *bbios;
    US *sbios;
    int i, j;
    US RomTableOff, FreqTablePtr, CdepthTablePtr;
    UL RegL;
    UB RegB;
    UB mask;

    /* setup bios area mappings and initial offsets */
    sbios = (US *) mapped_area;
    bbios = (UB *) mapped_area;

    RomTableOff = sbios[0x48 >> 1];
    FreqTablePtr = sbios[(RomTableOff >> 1) + 8];

    /* grab the CONFIG_CHIP_ID information */
    RegL = inl(ioCONFIG_CHIP_ID);
    StartupInfo.Chip_Type = (US) (RegL & 0xffff);
    StartupInfo.Chip_Class = (UB) ((RegL >> 16) && 0xff);
    StartupInfo.Chip_Rev = (UB) ((RegL >> 24) && 0xff);

    switch (StartupInfo.Chip_Type) {
    case 0xd7:
	if (StartupInfo.Chip_Rev <= 0x03)
	    StartupInfo.Chip_ID_Index = StartupInfo.Chip_Rev;
	else
	    StartupInfo.Chip_ID_Index = 0x04;
	break;
    case 0x57:
	if (StartupInfo.Chip_Rev == 0x01)
	    StartupInfo.Chip_ID_Index = 0x05;
	else
	    StartupInfo.Chip_ID_Index = 0x06;
	break;
    case 0x4354:
	StartupInfo.Chip_ID_Index = 0x07;
	break;
    case 0x4554:
	StartupInfo.Chip_ID_Index = 0x08;
	break;
    default:
	StartupInfo.Chip_ID_Index = 0x09;
    }

#ifdef REPORT
    if (StartupInfo.Chip_ID_Index < 0x07)
	fprintf(stdout, "M64: ATI Mach64 88800 %s detected.\n",
		M64_Chip_Name[StartupInfo.Chip_ID_Index]);
    else
	fprintf(stdout, "M64: ATI Mach64 88800 %s Rev. %x detected.\n",
		M64_Chip_Name[StartupInfo.Chip_ID_Index],
		StartupInfo.Chip_Rev);
#endif

    /* read the CONFIG_STAT0 and CONFIG_STAT1 */
    RegL = inl(ioCONFIG_STAT0);
    StartupInfo.Cfg_Bus_type = (UB) (RegL & 0x07);
    StartupInfo.Cfg_Ram_type = (UB) ((RegL >> 3) & 0x7);
    RegL = inl(ioCONFIG_STAT1);

#ifdef REPORT
    fprintf(stdout, "M64: Bus Type: %s\n",
	    M64_Bus_Name[StartupInfo.Cfg_Bus_type]);
    fprintf(stdout, "M64: Memory Type: %s\n",
	    M64_Ram_Name[StartupInfo.Cfg_Ram_type]);
#endif

    /* read memory configurations stuff */
    RegL = inl(ioMEM_CNTL);
    StartupInfo.Mem_Size = (UB) (RegL & 0x07);

#ifdef REPORT
    fprintf(stdout, "M64: Memory Installed: %d Kilobytes\n",
	    M64_Mem_Size[StartupInfo.Mem_Size]);
#endif

    /* read the dac type and sub type */
    RegB = inb(ioDAC_CNTL + 2);
    StartupInfo.Dac_Type = RegB & 0x07;
    RegB = inb(ioSCRATCH_REG1 + 1);
    StartupInfo.Dac_Subtype = (RegB & 0xf0) | StartupInfo.Dac_Type;

#ifdef REPORT
    fprintf(stdout, "M64: Dac Type: %d %s Sub Type: %d\n",
	    StartupInfo.Dac_Type,
	    M64_Dac_Name[StartupInfo.Dac_Type],
	    StartupInfo.Dac_Subtype);
#endif

    /* grab a pile of clock info */
    ClockInfo.ClockType = bbios[FreqTablePtr + 0];
    ClockInfo.MinFreq = sbios[(FreqTablePtr >> 1) + 1];
    ClockInfo.MaxFreq = sbios[(FreqTablePtr >> 1) + 2];
    ClockInfo.RefFreq = sbios[(FreqTablePtr >> 1) + 4];
    ClockInfo.RefDivdr = sbios[(FreqTablePtr >> 1) + 5];
    ClockInfo.N_adj = sbios[(FreqTablePtr >> 1) + 6];
    ClockInfo.Dram_Mem_Clk = sbios[(FreqTablePtr >> 1) + 8];
    ClockInfo.Vram_Mem_Clk = sbios[(FreqTablePtr >> 1) + 9];
    ClockInfo.Coproc_Mem_Clk = sbios[(FreqTablePtr >> 1) + 12];
    ClockInfo.CX_Clk = bbios[FreqTablePtr + 6];
    ClockInfo.VGA_Clk = bbios[FreqTablePtr + 7];
    ClockInfo.Mem_Clk_Entry = bbios[FreqTablePtr + 22];
    ClockInfo.SClk_Entry = bbios[FreqTablePtr + 23];
    ClockInfo.CX_DMcycle = bbios[RomTableOff + 0];
    ClockInfo.VGA_DMcycle = bbios[RomTableOff + 0];
    ClockInfo.CX_VMcycle = bbios[RomTableOff + 0];
    ClockInfo.VGA_VMcycle = bbios[RomTableOff + 0];
    CdepthTablePtr = sbios[(FreqTablePtr >> 1) - 3];
    FreqTablePtr = sbios[(FreqTablePtr >> 1) - 1];
    FreqTablePtr >>= 1;

    /* Read the default clocks */
    for (i = 0; i <= M64_CLOCK_MAX; i++)
	ClockInfo.ClkFreqTable[i] = sbios[FreqTablePtr + i];

    /* grab the color depth tables */
    cTabSize = bbios[CdepthTablePtr - 1];

#ifdef REPORT
    fprintf(stdout, "M64: ClockType: %d %s\n", ClockInfo.ClockType,
	    M64_Clock_Name[ClockInfo.ClockType]);
    fprintf(stdout, "M64: MinFreq: %d\n", ClockInfo.MinFreq);
    fprintf(stdout, "M64: MaxFreq: %d\n", ClockInfo.MaxFreq);
    fprintf(stdout, "M64: RefFreq: %d\n", ClockInfo.RefFreq);
    fprintf(stdout, "M64: RefDivdr: %d\n", ClockInfo.RefDivdr);
    fprintf(stdout, "M64: N_adj: %d\n", ClockInfo.N_adj);
    fprintf(stdout, "M64: DramMemClk: %d\n", ClockInfo.Dram_Mem_Clk);
    fprintf(stdout, "M64: VramMemClk: %d\n", ClockInfo.Vram_Mem_Clk);
    fprintf(stdout, "M64: CoprocMemClk: %d\n", ClockInfo.Coproc_Mem_Clk);
    fprintf(stdout, "M64: CX_Clk: %d\n", ClockInfo.CX_Clk);
    fprintf(stdout, "M64: VGA_Clk: %d\n", ClockInfo.VGA_Clk);
    fprintf(stdout, "M64: Mem_Clk_Entry: %d\n", ClockInfo.Mem_Clk_Entry);
    fprintf(stdout, "M64: SClk_Entry: %d\n", ClockInfo.SClk_Entry);
    fprintf(stdout, "M64: V/D_MCycle: %d\n", ClockInfo.CX_DMcycle);

    for (i = 0; i <= M64_CLOCK_MAX; i++) {
	if (i % 8 == 0)
	    fprintf(stdout, "\nM64: Clocks: ");
	fprintf(stdout, "%d.%d ", ClockInfo.ClkFreqTable[i] / 100,
		ClockInfo.ClkFreqTable[i] % 100);
    }
    fprintf(stdout, "\n");
#endif

    /* Get color depth tables that are valid for current dac */
    mask = 1 << StartupInfo.Dac_Type;

    for (j = i = 0; bbios[CdepthTablePtr + i] != 0; i += cTabSize) {
	if (bbios[CdepthTablePtr + i + 1] & mask) {
	    MaxCDT1[j].h_disp = bbios[CdepthTablePtr + i];
	    MaxCDT1[j].dacmask = bbios[CdepthTablePtr + i + 1];
	    MaxCDT1[j].ram_req = bbios[CdepthTablePtr + i + 2];
	    MaxCDT1[j].max_dot_clk = bbios[CdepthTablePtr + i + 3];
	    MaxCDT1[j].color_depth = bbios[CdepthTablePtr + i + 4];
	    ++j;
	}
    }
    MaxCDT1[j].h_disp = 0;

    /* Get second color depth table if exists that are valid subtype */
    CdepthTablePtr += i + 2;
    cTabSize = bbios[CdepthTablePtr - 1];
    for (j = i = 0; bbios[CdepthTablePtr + i] != 0; i += cTabSize) {
	if (bbios[CdepthTablePtr + i + 1] == StartupInfo.Dac_Subtype) {
	    MaxCDT2[j].h_disp = bbios[CdepthTablePtr + i];
	    MaxCDT2[j].dacmask = bbios[CdepthTablePtr + i + 1];
	    MaxCDT2[j].ram_req = bbios[CdepthTablePtr + i + 2];
	    MaxCDT2[j].max_dot_clk = bbios[CdepthTablePtr + i + 3];
	    MaxCDT2[j].color_depth = bbios[CdepthTablePtr + i + 4];
	    ++j;
	    fprintf(stdout, "%d\n", j);
	}
    }
    MaxCDT2[j].h_disp = 0;

    /* Filter out the useless entries based on bpp and memory */
    mask = StartupInfo.Cfg_Ram_type & VRAM_MASK;
    mask = (mask) ? 4 : 0;


    /* table 1 */
    for (i = 0, j = 0; MaxCDT1[i].h_disp != 0; i++) {
	/* pick out 8 bpp modes for now */
	switch (MaxCDT1[i].color_depth) {
	case BPP_8:
	    if ((MaxCDT1[i].ram_req >> mask) >
		StartupInfo.Mem_Size)
		break;

	    FreqCDT1[j].h_disp = MaxCDT1[i].h_disp;
	    FreqCDT1[j].dacmask = MaxCDT1[i].dacmask;
	    FreqCDT1[j].ram_req = MaxCDT1[i].ram_req;
	    FreqCDT1[j].max_dot_clk = MaxCDT1[i].max_dot_clk;
	    FreqCDT1[j].color_depth = MaxCDT1[i].color_depth;
	    j++;
	    break;
	case BPP_32:
	case BPP_16:
	case BPP_15:
	case BPP_4:
	default:
	    break;
	}
	FreqCDT1[j].h_disp = 0;
    }
    /* table 2 */
    for (i = 0, j = 0; MaxCDT2[i].h_disp != 0; i++) {
	/* pick out 8 bpp modes for now */
	switch (MaxCDT2[i].color_depth) {
	case BPP_8:
	    if ((MaxCDT2[i].ram_req >> mask) >
		StartupInfo.Mem_Size)
		break;

	    FreqCDT2[j].h_disp = MaxCDT2[i].h_disp;
	    FreqCDT2[j].dacmask = MaxCDT2[i].dacmask;
	    FreqCDT2[j].ram_req = MaxCDT2[i].ram_req;
	    FreqCDT2[j].max_dot_clk = MaxCDT2[i].max_dot_clk;
	    FreqCDT2[j].color_depth = MaxCDT2[i].color_depth;
	    j++;
	    break;
	case BPP_32:
	case BPP_16:
	case BPP_15:
	case BPP_4:
	default:
	    break;
	}
	FreqCDT2[j].h_disp = 0;
    }

#ifdef REPORT_CLKTAB
    fprintf(stdout, "Color Depth Table 1\n");
    for (i = 0; MaxCDT1[i].h_disp != 0; i++) {
	fprintf(stdout, "%d %d %d %d %d\n", MaxCDT1[i].h_disp,
		MaxCDT1[i].dacmask, MaxCDT1[i].ram_req,
		MaxCDT1[i].max_dot_clk, MaxCDT1[i].color_depth);
    }
    fprintf(stdout, "Color Depth Table 2 - if present\n");
    for (i = 0; MaxCDT2[i].h_disp != 0; i++) {
	fprintf(stdout, "%d %d %d %d %d\n", MaxCDT2[i].h_disp,
		MaxCDT2[i].dacmask, MaxCDT2[i].ram_req,
		MaxCDT2[i].max_dot_clk, MaxCDT2[i].color_depth);
    }
#endif
#ifdef REPORT
    fprintf(stdout, "Valid Color Depth Table 1\n");
    for (i = 0; FreqCDT1[i].h_disp != 0; i++) {
#ifdef REPORT_CLKTAB
	fprintf(stdout, "%d %d %d %d %d\n", FreqCDT1[i].h_disp,
		FreqCDT1[i].dacmask, FreqCDT1[i].ram_req,
		FreqCDT1[i].max_dot_clk, FreqCDT1[i].color_depth);
#endif
	fprintf(stdout, "Width: %d, MaxDotClk: %dMHz, Depth: %d bpp\n",
		FreqCDT1[i].h_disp * 8, FreqCDT1[i].max_dot_clk,
		M64_BPP[FreqCDT1[i].color_depth]);
    }
    fprintf(stdout, "Valid Color Depth Table 2 - if present\n");
    for (i = 0; FreqCDT2[i].h_disp != 0; i++) {
#ifdef REPORT_CLKTAB
	fprintf(stdout, "%d %d %d %d %d\n", FreqCDT2[i].h_disp,
		FreqCDT2[i].dacmask, FreqCDT2[i].ram_req,
		FreqCDT2[i].max_dot_clk, FreqCDT2[i].color_depth);
#endif
	fprintf(stdout, "Width: %d, MaxDotClk: %dMHz, Depth: %d bpp\n",
		FreqCDT2[i].h_disp * 8, FreqCDT2[i].max_dot_clk,
		M64_BPP[FreqCDT2[i].color_depth]);
    }
#endif
}

/*************************************************************************/

/* generates a quick register dump */
static void mach64_report(void)
{
    UL reg;

    reg = inl(ioBUS_CNTL);
    printf("BUS_CNTL: %lx ", reg);
    DUMP(reg);

    reg = inl(ioCONFIG_CHIP_ID);
    printf("CONFIG_CHIP_ID: %lx ", reg);
    DUMP(reg);


    reg = inl(ioCONFIG_CNTL);
    printf("CONFIG_CNTL: %lx ", reg);
    DUMP(reg);

    reg = inl(ioCONFIG_STAT0);
    printf("CONFIG_STAT0: %lx ", reg);
    DUMP(reg);

    reg = inl(ioCONFIG_STAT1);
    printf("CONFIG_STAT1: %lx ", reg);
    DUMP(reg);

    reg = inl(ioGEN_TEST_CNTL);
    printf("GEN_TEST_CNTL: %lx ", reg);
    DUMP(reg);

    reg = inl(ioMEM_CNTL);
    printf("MEM_CNTL: %lx ", reg);
    DUMP(reg);

    reg = inl(ioMEM_VGA_RP_SEL);
    printf("MEM_VGA_RP_SEL: %lx ", reg);
    DUMP(reg);

    reg = inl(ioMEM_VGA_WP_SEL);
    printf("MEM_VGA_WP_SEL: %lx ", reg);
    DUMP(reg);

    reg = inl(ioDAC_CNTL);
    printf("DAC_CNTL: %lx ", reg);
    DUMP(reg);

    reg = inl(ioCLOCK_SEL_CNTL);
    printf("CLOCK_CNTL: %lx ", reg);
    DUMP(reg);

    reg = inl(ioCRTC_GEN_CNTL);
    printf("CRTC_GEN: %lx ", reg);
    DUMP(reg);

    reg = inl(ioSCRATCH_REG0);
    printf("SCRATCH 0: %lx ", reg);
    DUMP(reg);

    reg = inl(ioSCRATCH_REG1);
    printf("SCRATCH 1: %lx ", reg);
    DUMP(reg);

    reg = inl(ioCRTC_H_SYNC_STRT_WID);
    printf("H_S_S_W: %lx ", reg);
    DUMP(reg);
}

/*************************************************************************/

/* program the ati68860 ramdac -- standard on old mach64's */
static int mach64_dac_ati68860(US c_depth, US accelmode)
{
    US gmode, dsetup, temp, mask;

    /* filter pixel depth */
    if (c_depth == BPP_8) {
	gmode = 0x83;
	dsetup = 0x61;
    } else {
	printf("unsupported bpp\n");
	exit(1);
    }

    /* if we are in vga mode */
    if (!accelmode) {
	gmode = 0x80;
	dsetup = 0x60;
    }
    temp = inb(ioDAC_CNTL);
    outb(ioDAC_CNTL, (temp & ~DAC_EXT_SEL_RS2) | DAC_EXT_SEL_RS3);

    outb(ioDAC_REGS + 2, 0x1d);
    outb(ioDAC_REGS + 3, gmode);
    outb(ioDAC_REGS, 0x02);

    temp = inb(ioDAC_CNTL);
    outb(ioDAC_CNTL, temp | DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3);

    if (M64_Mem_Size[StartupInfo.Mem_Size] < 1024)
	mask = 0x04;
    else if (M64_Mem_Size[StartupInfo.Mem_Size] == 1024)
	mask = 0x08;
    else
	mask = 0x0c;

    temp = inb(ioDAC_REGS);
    outb(ioDAC_REGS, (dsetup | mask) | (temp & 0x80));
    temp = inb(ioDAC_CNTL);
    outb(ioDAC_CNTL, (temp & ~(DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3)));

    /* pixel delay from ati */
    temp = inb(ioCRTC_H_SYNC_STRT_WID + 1);
    temp = 0x1c;		/*was temp+= */
    outb(ioCRTC_H_SYNC_STRT_WID + 1, temp & 0x07);
    temp >>= 3;
    temp = temp + inb(ioCRTC_H_SYNC_STRT_WID);
    outb(ioCRTC_H_SYNC_STRT_WID, temp);

    return 0;
}

/* general ramdac program */
static void mach64_dac_program(US c_depth, US accelmode, US dotclock)
{
    US temp, mux;

    /* check to see if we are using an accelerator mode and turn on
       the extended mode display and crtc */
    if (accelmode) {
	temp = inb(CRTC_GEN_CNTL) & ~CRTC_PIX_BY_2_EN;
	outb(ioCRTC_GEN_CNTL, temp);
	outb(ioCRTC_GEN_CNTL + 3, CRTC_EXT_DISP_EN | CRTC_EXT_EN);
    } else {
	outb(ioCRTC_GEN_CNTL + 3, 0);
    }

    temp = inb(ioCRTC_GEN_CNTL + 3);
    outb(ioCRTC_GEN_CNTL + 3, temp | CRTC_EXT_DISP_EN);

    /* switch on the dac type */
    mux = mach64_dac_ati68860(c_depth, accelmode);

    inb(ioDAC_REGS);
    outb(ioCRTC_GEN_CNTL + 3, temp);

    temp = inb(ioCRTC_GEN_CNTL) & ~CRTC_PIX_BY_2_EN;
    if (mux)
	temp = temp | CRTC_PIX_BY_2_EN;
    outb(ioCRTC_GEN_CNTL, temp);
}

/* setup the crtc registers */
static void mach64_crtc_programming(US mode, US c_depth, US refrate)
{
    CRTC_Table *pcrtc;
    UB temp3, temp0;

    /* fix to mode 640x480 */
    pcrtc = &CRTC_tinfo[0];

    /* determine the fifo value and dot clock */
    pcrtc->fifo_vl = 0x02;
    pcrtc->pdot_clock = pcrtc->dot_clock;

    temp3 = inb(ioCRTC_GEN_CNTL + 3);
    temp0 = inb(ioCRTC_GEN_CNTL + 0);
    outb(ioCRTC_GEN_CNTL + 3, temp3 & ~CRTC_EXT_EN);

    /* here would would program the clock... but we won't */

    /* horizontal */
    outb(CRTC_H_TOTAL, pcrtc->h_total);
    outb(CRTC_H_DISP, pcrtc->h_disp);
    outb(CRTC_H_SYNC_STRT, pcrtc->h_sync_strt);
    outb(CRTC_H_SYNC_WID, pcrtc->h_sync_wid);

    printf("CRTC_H_TD: %x\n", inl(ioCRTC_H_TOTAL_DISP));
    printf("CRTC_H_SN: %x\n", inl(ioCRTC_H_SYNC_STRT_WID));

    /* vertical */
    outw(CRTC_V_TOTAL, pcrtc->v_total);
    outw(CRTC_V_DISP, pcrtc->v_disp);
    outw(CRTC_V_SYNC_STRT, pcrtc->v_sync_strt);
    outb(CRTC_V_SYNC_WID, pcrtc->v_sync_wid);

    printf("CRTC_V_TD: %lx\n", (UL) inl(ioCRTC_V_TOTAL_DISP));
    printf("CRTC_V_SN: %lx\n", (UL) inl(ioCRTC_V_SYNC_STRT_WID));

    /* clock stuff */
    /* 50/2 */
    pcrtc->clock_cntl = 0x00 | 0x10;
    /* CX clock */
    pcrtc->clock_cntl = 0x08;
    /*outb(CLOCK_CNTL,pcrtc->clock_cntl|CLOCK_STROBE); */
    outb(CLOCK_CNTL, pcrtc->clock_cntl);
    printf("CLK: %lx\n", (UL) inl(ioCLOCK_SEL_CNTL));

    /* overscan */
    outb(OVR_WID_LEFT, 0);
    outb(OVR_WID_RIGHT, 0);
    outb(OVR_WID_BOTTOM, 0);
    outb(OVR_WID_TOP, 0);
    outb(OVR_CLR_8, 0);
    outb(OVR_CLR_B, 0);
    outb(OVR_CLR_G, 0);
    outb(OVR_CLR_R, 0);

    /* pitch */
    outl(ioCRTC_OFF_PITCH, (UL) 80 << 22);
    mreg[mSRC_OFF_PITCH / 4 + 0xC00 / 4] = (UL) 80 << 22;
    mreg[mDST_OFF_PITCH / 4 + 0xC00 / 4] = (UL) 80 << 22;

    /* turn on display */
    outb(ioCRTC_GEN_CNTL + 0, temp0 &
	 ~(CRTC_PIX_BY_2_EN | CRTC_DBL_SCAN_EN | CRTC_INTERLACE_EN));
    outb(ioCRTC_GEN_CNTL + 1, c_depth);
    outb(ioCRTC_GEN_CNTL + 2, pcrtc->fifo_vl);
    outb(ioCRTC_GEN_CNTL + 3, temp3 | CRTC_EXT_DISP_EN | CRTC_EXT_EN);

    mreg[mDP_PIX_WIDTH / 4 + 0xC00 / 4] = (UL) 0x01020202;
    mreg[mDP_CHAIN_MASK / 4 + 0xC00 / 4] = (UL) 0x8080;
    mreg[mCONTEXT_MASK / 4 + 0xC00 / 4] = (UL) 0xffffffff;
    /* woudn't a macro be nice */
    mreg[mDST_CNTL / 4 + 0xC00 / 4] = (UL) 0x00;

    printf("CRTC_GEN_IN_CRTC: %x\n", inl(ioCRTC_GEN_CNTL));
    /* configure ramdac */
    mach64_dac_program(c_depth, 1, pcrtc->pdot_clock);

    mach64_report();
}

/*************************************************************************/
static int mach64_saveregs(UB * regs)
{
    int i, retval;
    UL temp;
    UB tb;

    /* store all the crtc, clock, memory registers */
    retval = EXT;
    for (i = 0; i < 13; i++) {
	temp = inl(sr_accel[i]);
	printf("Saved: %d %lx\n", i, temp);
	regs[retval++] = (UB) temp & 0x000000ff;
	temp = temp >> 8;
	regs[retval++] = (UB) temp & 0x000000ff;
	temp = temp >> 8;
	regs[retval++] = (UB) temp & 0x000000ff;
	temp = temp >> 8;
	regs[retval++] = (UB) temp & 0x000000ff;

    }

    /* store the dac --68860 */
    tb = inb(ioDAC_CNTL);
    outb(ioDAC_CNTL, (tb & ~DAC_EXT_SEL_RS2) | DAC_EXT_SEL_RS3);
    regs[retval++] = (UB) inb(ioDAC_REGS + 2);
    regs[retval++] = (UB) inb(ioDAC_REGS + 3);
    regs[retval++] = (UB) inb(ioDAC_REGS);
    tb = inb(ioDAC_CNTL);
    outb(ioDAC_CNTL, tb | DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3);
    regs[retval++] = (UB) inb(ioDAC_REGS);
    tb = inb(ioDAC_CNTL);
    outb(ioDAC_CNTL, (tb & ~(DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3)));

    return retval - EXT + 1;
}

static void mach64_setregs(const UB * regs, int mode)
{
    int i, retval;
    UL temp;
    UB tb;

    outb(0x3ce, 6);
    outb(0x3cf, 0);
    outb(ioCONFIG_CNTL, (inb(ioCONFIG_CNTL) & 0xfb));
    outb(ioCRTC_GEN_CNTL + 3, 0);

    return;
    /* restore all the crtc, clock, memory registers */
    retval = EXT;
    for (i = 0; i < 13; i++) {
	temp = (UL) 0;
	temp = temp | (regs[retval++] << 24);
	temp = temp | (regs[retval++] << 16);
	temp = temp | (regs[retval++] << 8);
	temp = temp | regs[retval++];
	outl(sr_accel[i], temp);

    }

    /* store the dac --68860 */
    tb = inb(ioDAC_CNTL);
    outb(ioDAC_CNTL, (tb & ~DAC_EXT_SEL_RS2) | DAC_EXT_SEL_RS3);
    outb(ioDAC_REGS + 2, regs[retval++]);
    outb(ioDAC_REGS + 3, regs[retval++]);
    outb(ioDAC_REGS, regs[retval++]);
    tb = inb(ioDAC_CNTL);
    outb(ioDAC_CNTL, tb | DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3);
    outb(ioDAC_REGS, regs[retval++]);
    tb = inb(ioDAC_CNTL);
    outb(ioDAC_CNTL, (tb & ~(DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3)));

}

static void mach64_getmodeinfo(int mode, vga_modeinfo * modeinfo)
{
    /*__svgalib_vga_driverspecs.getmodeinfo(mode, modeinfo); */
    modeinfo->startaddressrange = 0xfffff;
/*      modeinfo->startaddressrange=0xfffffffc&(2048*1024-1);
 */

    modeinfo->maxpixels = 640 * 480;
/*      modeinfo->bytesperpixel=infotable[mode].bytesperpixel;
   modeinfo->linewidth_unit=8;
   modeinfo->linewidth=infotable[mode].xbytes;
   modeinfo->maxlogicalwidth=0xff*8*modeinfo->bytesperpixel;
   modeinfo->startaddressrange=0xffc00000;
 */
}

static int mach64_modeavailable(int mode)
{
/*
   if (mode>__svgalib_max_modes)
 */
    if (mode > __GLASTMODE)
	return 0;
    if (mode < G640x480x256 || mode == G720x348x2)
	return __svgalib_vga_driverspecs.modeavailable(mode);
    else if (mode != G640x480x256)
	return 0;

    return SVGADRV;
}

static int mach64_setmode(int mode, int prv_mode)
{
    const UB *regs = NULL;
    UB di;

    mach64_setregs(regs, mode);

    if (mode < G640x480x256 || mode == G720x348x2)
	return __svgalib_vga_driverspecs.setmode(mode, prv_mode);

    if (mode == G640x480x256) {

	/* I don't think this is necessary since I dont even
	   use the vga */
	/* set packed pixel register */
	outb(ATIPORT, ATISEL(0x30));
	di = inb(ATIPORT);
	printf("pre ext: %d\n", di);
	outb(ATIPORT, ATISEL(0x30));
	outb(ATIPORT, di | 0x20);

	/* all maps */
	outb(0x3c4, 2);
	outb(0x3c5, 0x0f);

	/* linear */
	outb(ATIPORT, ATISEL(0x36));
	di = inb(ATIPORT);
	outb(ATIPORT, ATISEL(0x36));
	outb(ATIPORT, di | 0x04);

	/* turn off memory boundary */
	outl(ioMEM_CNTL, inl(ioMEM_CNTL) & 0xfff8ffff);

	/* disable interrups */
	outl(ioCRTC_INT_CNTL, 0);

	/* 8 bit dac */
	outb(ioDAC_CNTL + 1, inb(ioDAC_CNTL + 1) | 0x01);
	outb(ioCRTC_GEN_CNTL + 3, 3);
	/* setup small aperature */
	mach64_small_aperature();


	mach64_setpage(0);
#ifdef DEBUG
	printf("About to call crtc programming...\n");
#endif
	mach64_crtc_programming(0, BPP_8, 60);
	di = 0;
/*              for (di=0;di<32;di++){ 
   mach64_setpage(di);usleep(10000);
   for (i=0;i<64*1024;i++){
   graph_mem[i]=i%16;}
   }
	 *//*            for (j=0;j<1024*2048/8;j+=640){
	   usleep(1);
	   outw(ioCRTC_OFF_PITCH,j/8);
	   }  
	 *//*            outw(ioCRTC_OFF_PITCH,4000);
	   outb(ioCRTC_OFF_PITCH+2,inb(ioCRTC_OFF_PITCH+2)|0x10);
	   outw(ioCRTC_OFF_PITCH,80*480);
	 */ outw(ioCRTC_OFF_PITCH, 0);
	mach64_setpage(0);
/*              for(j=0;j<64000;j++){
   graph_mem[j]=0x0c;
   }
   sleep(2);
 */ mach64_setpage(0);
	mach64_report();
	return 0;
    }
    return 1;


}

static void mach64_setdisplaystart(int address)
{
    /*__svgalib_vga_driverspecs.setdisplaystart(address); */
}

static void mach64_setlogicalwidth(int width)
{
    /*__svgalib_vga_driverspecs.setlogicalwidth(width); */
}

static void mach64_setpage(int page)
{
    printf("PAGE: %d\n", page);
    outb(ioMEM_VGA_WP_SEL, 255 - (page * 2));
    outb(ioMEM_VGA_WP_SEL + 2, 255 - ((page * 2) + 1));
    outb(ioMEM_VGA_RP_SEL, 255 - (page * 2));
    outb(ioMEM_VGA_RP_SEL + 2, 255 - ((page * 2) + 1));
}

static void mach64_small_aperature(void)
{
    int i;

    /* 256 color mode */
    outb(0x3ce, 5);
    printf("pre 5:%d\n", inb(0x3cf));
    outb(0x3ce, 5);
    outb(0x3cf, 0x60);
    outb(0x3ce, 5);
    printf("afe 5:%d\n", inb(0x3cf));
    /* APA mode with 128K at A0000 */
    outb(0x3ce, 6);
    printf("pre 6:%d\n", inb(0x3cf));
    outb(0x3ce, 6);
    outb(0x3cf, 1);
    outb(0x3ce, 6);
    printf("afe 6:%d\n", inb(0x3cf));

    /* setup small aperature */
    outb(ioCONFIG_CNTL, (inb(ioCONFIG_CNTL) | 0x04) & 0xff);
    /* map ram */
    mreg = (UL *) mmap((caddr_t) 0,
		       4 * 1024,
		       PROT_READ | PROT_WRITE,
		       MAP_SHARED | MAP_FIXED,
		       __svgalib_mem_fd,
		       (off_t) ((UB *) 0xbf000));
    if (mreg < 0) {
	printf("Mapping failed\n");
	exit(1);
    }
    printf("IOPORT: %x\n", inl(ioCONFIG_STAT0));
    for (i = 0; i <= 0xce; i++) {
	printf("MEMA: %x %lx\n", i, mreg[i + 0xc00 / 4]);
    }
}
