/*
Rage 128 chipset driver 
*/

#include <stdlib.h>
#include <stdio.h>		
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include "vga.h"
#include "libvga.h"
#include "driver.h"
#include "timing.h"
#include "vgaregs.h"
#include "interface.h"
#include "accel.h"
#include "vgapci.h"
#include "r128_reg.h"

typedef unsigned int CARD32;
typedef unsigned short CARD16;
typedef int Bool;

static int r128_ramtype;
static int BusCntl, CRTOnly, HasPanelRegs;

typedef struct {
    CARD16        reference_freq;
    CARD16        reference_div;
    CARD32        min_pll_freq;
    CARD32        max_pll_freq;
    CARD16        xclk;
} R128PLLRec, *R128PLLPtr;

typedef struct {
				/* Common registers */
    CARD32     ovr_clr;
    CARD32     ovr_wid_left_right;
    CARD32     ovr_wid_top_bottom;
    CARD32     ov0_scale_cntl;
    CARD32     mpp_tb_config;
    CARD32     mpp_gp_config;
    CARD32     subpic_cntl;
    CARD32     viph_control;
    CARD32     i2c_cntl_1;
    CARD32     gen_int_cntl;
    CARD32     cap0_trig_cntl;
    CARD32     cap1_trig_cntl;
    CARD32     bus_cntl;
    CARD32     config_cntl;
    CARD32     mem_vga_wp_sel;
    CARD32     mem_vga_rp_sel;
				/* Other registers to save for VT switches */
    CARD32     dp_datatype;
    CARD32     gen_reset_cntl;
    CARD32     clock_cntl_index;
    CARD32     amcgpio_en_reg;
    CARD32     amcgpio_mask;
				/* CRTC registers */
    CARD32     crtc_gen_cntl;
    CARD32     crtc_ext_cntl;
    CARD32     dac_cntl;
    CARD32     crtc_h_total_disp;
    CARD32     crtc_h_sync_strt_wid;
    CARD32     crtc_v_total_disp;
    CARD32     crtc_v_sync_strt_wid;
    CARD32     crtc_offset;
    CARD32     crtc_offset_cntl;
    CARD32     crtc_pitch;
				/* CRTC2 registers */
    CARD32     crtc2_gen_cntl;
				/* Flat panel registers */
    CARD32     fp_crtc_h_total_disp;
    CARD32     fp_crtc_v_total_disp;
    CARD32     fp_gen_cntl;
    CARD32     fp_h_sync_strt_wid;
    CARD32     fp_horz_stretch;
    CARD32     fp_panel_cntl;
    CARD32     fp_v_sync_strt_wid;
    CARD32     fp_vert_stretch;
    CARD32     lvds_gen_cntl;
    CARD32     tmds_crc;
				/* Computed values for PLL */
    CARD32     dot_clock_freq;
    CARD32     pll_output_freq;
    int        feedback_div;
    int        post_div;
				/* PLL registers */
    CARD32     ppll_ref_div;
    CARD32     ppll_div_3;
    CARD32     htotal_cntl;
				/* DDA register */
    CARD32     dda_config;
    CARD32     dda_on_off;
    CARD32     vga_dda_config;
    CARD32     vga_dda_on_off;
				/* Pallet */
    Bool       palette_valid;
    CARD32     palette[256];
} R128SaveRec, *R128SavePtr;

typedef struct {        /* All values in XCLKS    */
    int  ML;            /* Memory Read Latency    */
    int  MB;            /* Memory Burst Length    */
    int  Trcd;          /* RAS to CAS delay       */
    int  Trp;           /* RAS percentage         */
    int  Twr;           /* Write Recovery         */
    int  CL;            /* CAS Latency            */
    int  Tr2w;          /* Read to Write Delay    */
    int  Rloop;         /* Loop Latency           */
    int  Rloop_fudge;   /* Add to ML to get Rloop */
    char *name;
} R128RAMRec, *R128RAMPtr;

#define R128_TOTAL_REGS (VGA_TOTAL_REGS + sizeof(R128SaveRec))
static int R128MinBits(int val)
{
    int bits;

    if (!val) return 1;
    for (bits = 0; val; val >>= 1, ++bits);
    return bits;
}
static int R128Div(int n, int d)
{
    return (n + (d / 2)) / d;
}
static R128PLLRec pll;

static R128RAMRec ram[] = {        /* Memory Specifications
				   From RAGE 128 Software Development
				   Manual (Technical Reference Manual P/N
				   SDK-G04000 Rev 0.01), page 3-21.  */
    { 4, 4, 3, 3, 1, 3, 1, 16, 12, "128-bit SDR SGRAM 1:1" },
    { 4, 8, 3, 3, 1, 3, 1, 17, 13, "64-bit SDR SGRAM 1:1" },
    { 4, 4, 1, 2, 1, 2, 1, 16, 12, "64-bit SDR SGRAM 2:1" },
    { 4, 4, 3, 3, 2, 3, 1, 16, 12, "64-bit DDR SGRAM" },
};

unsigned R128INPLL(int addr)
{
    OUTREG8(R128_CLOCK_CNTL_INDEX, addr & 0x1f);
    return INREG(R128_CLOCK_CNTL_DATA);
}

void R128WaitForVerticalSync(void)
{
    int           i;

    OUTREG(R128_GEN_INT_STATUS, R128_VSYNC_INT_AK);
    for (i = 0; i < R128_TIMEOUT; i++) {
	if (INREG(R128_GEN_INT_STATUS) & R128_VSYNC_INT) break;
    }
}

/* Blank screen. */
static void R128Blank(void)
{
    OUTREGP(R128_CRTC_EXT_CNTL, R128_CRTC_DISPLAY_DIS, ~R128_CRTC_DISPLAY_DIS);
}

/* Unblank screen. */
static void R128Unblank(void)
{
    OUTREGP(R128_CRTC_EXT_CNTL, 0, ~R128_CRTC_DISPLAY_DIS);
}

static void R128RestoreCommonRegisters(R128SavePtr restore)
{
    OUTREG(R128_OVR_CLR,              restore->ovr_clr);
    OUTREG(R128_OVR_WID_LEFT_RIGHT,   restore->ovr_wid_left_right);
    OUTREG(R128_OVR_WID_TOP_BOTTOM,   restore->ovr_wid_top_bottom);
    OUTREG(R128_OV0_SCALE_CNTL,       restore->ov0_scale_cntl);
    OUTREG(R128_MPP_TB_CONFIG,        restore->mpp_tb_config );
    OUTREG(R128_MPP_GP_CONFIG,        restore->mpp_gp_config );
    OUTREG(R128_SUBPIC_CNTL,          restore->subpic_cntl);
    OUTREG(R128_VIPH_CONTROL,         restore->viph_control);
    OUTREG(R128_I2C_CNTL_1,           restore->i2c_cntl_1);
    OUTREG(R128_GEN_INT_CNTL,         restore->gen_int_cntl);
    OUTREG(R128_CAP0_TRIG_CNTL,       restore->cap0_trig_cntl);
    OUTREG(R128_CAP1_TRIG_CNTL,       restore->cap1_trig_cntl);
    OUTREG(R128_BUS_CNTL,             restore->bus_cntl);
    OUTREG(R128_CONFIG_CNTL,          restore->config_cntl);
    OUTREG(R128_MEM_VGA_WP_SEL,	      restore->mem_vga_wp_sel);
    OUTREG(R128_MEM_VGA_RP_SEL,	      restore->mem_vga_rp_sel);

}

/* Write CRTC registers. */
static void R128RestoreCrtcRegisters(R128SavePtr restore)
{
    OUTREG(R128_CRTC_GEN_CNTL,        restore->crtc_gen_cntl);

    OUTREGP(R128_CRTC_EXT_CNTL, restore->crtc_ext_cntl,
	    R128_CRTC_VSYNC_DIS | R128_CRTC_HSYNC_DIS | R128_CRTC_DISPLAY_DIS);

    OUTREGP(R128_DAC_CNTL, restore->dac_cntl,
	    R128_DAC_RANGE_CNTL | R128_DAC_BLANKING);

    OUTREG(R128_CRTC_H_TOTAL_DISP,    restore->crtc_h_total_disp);
    OUTREG(R128_CRTC_H_SYNC_STRT_WID, restore->crtc_h_sync_strt_wid);
    OUTREG(R128_CRTC_V_TOTAL_DISP,    restore->crtc_v_total_disp);
    OUTREG(R128_CRTC_V_SYNC_STRT_WID, restore->crtc_v_sync_strt_wid);
    OUTREG(R128_CRTC_OFFSET,          restore->crtc_offset);
    OUTREG(R128_CRTC_OFFSET_CNTL,     restore->crtc_offset_cntl);
    OUTREG(R128_CRTC_PITCH,           restore->crtc_pitch);
}

/* Write flat panel registers */
static void R128RestoreFPRegisters(R128SavePtr restore)
{
    CARD32        tmp;

    OUTREG(R128_CRTC2_GEN_CNTL,       restore->crtc2_gen_cntl);
    OUTREG(R128_FP_CRTC_H_TOTAL_DISP, restore->fp_crtc_h_total_disp);
    OUTREG(R128_FP_CRTC_V_TOTAL_DISP, restore->fp_crtc_v_total_disp);
    OUTREG(R128_FP_GEN_CNTL,          restore->fp_gen_cntl);
    OUTREG(R128_FP_H_SYNC_STRT_WID,   restore->fp_h_sync_strt_wid);
    OUTREG(R128_FP_HORZ_STRETCH,      restore->fp_horz_stretch);
    OUTREG(R128_FP_PANEL_CNTL,        restore->fp_panel_cntl);
    OUTREG(R128_FP_V_SYNC_STRT_WID,   restore->fp_v_sync_strt_wid);
    OUTREG(R128_FP_VERT_STRETCH,      restore->fp_vert_stretch);
    OUTREG(R128_TMDS_CRC,             restore->tmds_crc);

    tmp = INREG(R128_LVDS_GEN_CNTL);
    if ((tmp & (R128_LVDS_ON | R128_LVDS_BLON)) ==
	(restore->lvds_gen_cntl & (R128_LVDS_ON | R128_LVDS_BLON))) {
	OUTREG(R128_LVDS_GEN_CNTL, restore->lvds_gen_cntl);
    } else {
	if (restore->lvds_gen_cntl & (R128_LVDS_ON | R128_LVDS_BLON)) {
	    OUTREG(R128_LVDS_GEN_CNTL, restore->lvds_gen_cntl & ~R128_LVDS_BLON);
//	    usleep(R128PTR(pScrn)->PanelPwrDly * 1000);
	    OUTREG(R128_LVDS_GEN_CNTL, restore->lvds_gen_cntl);
	} else {
	    OUTREG(R128_LVDS_GEN_CNTL, restore->lvds_gen_cntl | R128_LVDS_BLON);
//	    usleep(R128PTR(pScrn)->PanelPwrDly * 1000);
	    OUTREG(R128_LVDS_GEN_CNTL, restore->lvds_gen_cntl);
	}
    }
}

static void R128PLLWaitForReadUpdateComplete(void)
{
    while (INPLL(R128_PPLL_REF_DIV) & R128_PPLL_ATOMIC_UPDATE_R);
}

static void R128PLLWriteUpdate(void)
{
    OUTPLLP(R128_PPLL_REF_DIV, R128_PPLL_ATOMIC_UPDATE_W, 0xffff);
}

/* Write PLL registers. */
static void R128RestorePLLRegisters(R128SavePtr restore)
{
    OUTREGP(R128_CLOCK_CNTL_INDEX, R128_PLL_DIV_SEL, 0xffff);

    OUTPLLP(
	    R128_PPLL_CNTL,
	    R128_PPLL_RESET
	    | R128_PPLL_ATOMIC_UPDATE_EN,
	    0xffff);

    R128PLLWaitForReadUpdateComplete();
    OUTPLLP(R128_PPLL_REF_DIV,
	    restore->ppll_ref_div, ~R128_PPLL_REF_DIV_MASK);
    R128PLLWriteUpdate();

    R128PLLWaitForReadUpdateComplete();
    OUTPLLP(R128_PPLL_DIV_3,
	    restore->ppll_div_3, ~R128_PPLL_FB3_DIV_MASK);
    R128PLLWriteUpdate();
    OUTPLLP(R128_PPLL_DIV_3,
	    restore->ppll_div_3, ~R128_PPLL_POST3_DIV_MASK);
    R128PLLWriteUpdate();

    R128PLLWaitForReadUpdateComplete();
    OUTPLL(R128_HTOTAL_CNTL, restore->htotal_cntl);
    R128PLLWriteUpdate();

    OUTPLLP( R128_PPLL_CNTL, 0, ~R128_PPLL_RESET);

}

/* Write DDA registers. */
static void R128RestoreDDARegisters(R128SavePtr restore)
{
    OUTREG(R128_DDA_CONFIG, restore->dda_config);
    OUTREG(R128_DDA_ON_OFF, restore->dda_on_off);
//    OUTREG(R128_VGA_DDA_CONFIG, restore->vga_dda_config);
//    OUTREG(R128_VGA_DDA_ON_OFF, restore->vga_dda_on_off);
}

/* Write palette data. */
static void R128RestorePalette( R128SavePtr restore)
{
    int           i;

    if (!restore->palette_valid) return;

    /* Select palette 0 (main CRTC) if using FP-enabled chip */
//    if (info->HasPanelRegs) PAL_SELECT(0);

    OUTPAL_START(0);
    for (i = 0; i < 256; i++) OUTPAL_NEXT_CARD32(restore->palette[i]);
}

/* Write out state to define a new video mode.  */
static void R128RestoreMode(R128SavePtr restore)
{
    R128Blank();

    OUTREG(R128_AMCGPIO_MASK,     restore->amcgpio_mask);
    OUTREG(R128_AMCGPIO_EN_REG,   restore->amcgpio_en_reg);
    OUTREG(R128_CLOCK_CNTL_INDEX, restore->clock_cntl_index);
    OUTREG(R128_GEN_RESET_CNTL,   restore->gen_reset_cntl);
    OUTREG(R128_DP_DATATYPE,      restore->dp_datatype);

    R128RestoreCommonRegisters( restore);
    R128RestoreCrtcRegisters( restore);
//    if (info->HasPanelRegs)
//	R128RestoreFPRegisters(restore);
//    if (!info->HasPanelRegs || info->CRTOnly)
	R128RestorePLLRegisters(restore);
    R128RestoreDDARegisters(restore);
    R128RestorePalette(restore);
}

/* Read common registers. */
static void R128SaveCommonRegisters(R128SavePtr save)
{
    save->ovr_clr            = INREG(R128_OVR_CLR);
    save->ovr_wid_left_right = INREG(R128_OVR_WID_LEFT_RIGHT);
    save->ovr_wid_top_bottom = INREG(R128_OVR_WID_TOP_BOTTOM);
    save->ov0_scale_cntl     = INREG(R128_OV0_SCALE_CNTL);
    save->mpp_tb_config      = INREG(R128_MPP_TB_CONFIG);
    save->mpp_gp_config      = INREG(R128_MPP_GP_CONFIG);
    save->subpic_cntl        = INREG(R128_SUBPIC_CNTL);
    save->viph_control       = INREG(R128_VIPH_CONTROL);
    save->i2c_cntl_1         = INREG(R128_I2C_CNTL_1);
    save->gen_int_cntl       = INREG(R128_GEN_INT_CNTL);
    save->cap0_trig_cntl     = INREG(R128_CAP0_TRIG_CNTL);
    save->cap1_trig_cntl     = INREG(R128_CAP1_TRIG_CNTL);
    save->bus_cntl           = INREG(R128_BUS_CNTL);
    save->config_cntl        = INREG(R128_CONFIG_CNTL);
    save->mem_vga_wp_sel     = INREG(R128_MEM_VGA_WP_SEL);
    save->mem_vga_rp_sel     = INREG(R128_MEM_VGA_RP_SEL);
}

/* Read CRTC registers. */
static void R128SaveCrtcRegisters(R128SavePtr save)
{
    save->crtc_gen_cntl        = INREG(R128_CRTC_GEN_CNTL);
    save->crtc_ext_cntl        = INREG(R128_CRTC_EXT_CNTL);
    save->dac_cntl             = INREG(R128_DAC_CNTL);
    save->crtc_h_total_disp    = INREG(R128_CRTC_H_TOTAL_DISP);
    save->crtc_h_sync_strt_wid = INREG(R128_CRTC_H_SYNC_STRT_WID);
    save->crtc_v_total_disp    = INREG(R128_CRTC_V_TOTAL_DISP);
    save->crtc_v_sync_strt_wid = INREG(R128_CRTC_V_SYNC_STRT_WID);
    save->crtc_offset          = INREG(R128_CRTC_OFFSET);
    save->crtc_offset_cntl     = INREG(R128_CRTC_OFFSET_CNTL);
    save->crtc_pitch           = INREG(R128_CRTC_PITCH);
}

/* Read flat panel registers */
static void R128SaveFPRegisters(R128SavePtr save)
{
    save->crtc2_gen_cntl       = INREG(R128_CRTC2_GEN_CNTL);
    save->fp_crtc_h_total_disp = INREG(R128_FP_CRTC_H_TOTAL_DISP);
    save->fp_crtc_v_total_disp = INREG(R128_FP_CRTC_V_TOTAL_DISP);
    save->fp_gen_cntl          = INREG(R128_FP_GEN_CNTL);
    save->fp_h_sync_strt_wid   = INREG(R128_FP_H_SYNC_STRT_WID);
    save->fp_horz_stretch      = INREG(R128_FP_HORZ_STRETCH);
    save->fp_panel_cntl        = INREG(R128_FP_PANEL_CNTL);
    save->fp_v_sync_strt_wid   = INREG(R128_FP_V_SYNC_STRT_WID);
    save->fp_vert_stretch      = INREG(R128_FP_VERT_STRETCH);
    save->lvds_gen_cntl        = INREG(R128_LVDS_GEN_CNTL);
    save->tmds_crc             = INREG(R128_TMDS_CRC);
}

/* Read PLL registers. */
static void R128SavePLLRegisters(R128SavePtr save)
{
    save->ppll_ref_div         = INPLL(R128_PPLL_REF_DIV);
    save->ppll_div_3           = INPLL(R128_PPLL_DIV_3);
    save->htotal_cntl          = INPLL(R128_HTOTAL_CNTL);
}

/* Read DDA registers. */
static void R128SaveDDARegisters(R128SavePtr save)
{
    save->dda_config           = INREG(R128_DDA_CONFIG);
    save->dda_on_off           = INREG(R128_DDA_ON_OFF);
    save->vga_dda_config           = INREG(R128_VGA_DDA_CONFIG);
    save->vga_dda_on_off           = INREG(R128_VGA_DDA_ON_OFF);
}

/* Read palette data. */
static void R128SavePalette(R128SavePtr save)
{
    int           i;

    /* Select palette 0 (main CRTC) if using FP-enabled chip */
//    if (info->HasPanelRegs) PAL_SELECT(0);

    INPAL_START(0);
    for (i = 0; i < 256; i++) save->palette[i] = INPAL_NEXT();
    save->palette_valid = 1;
}

/* Save state that defines current video mode. */
static void R128SaveMode(R128SavePtr save)
{
    R128SaveCommonRegisters(save);
    R128SaveCrtcRegisters(save);
//    if (R128PTR(pScrn)->HasPanelRegs)
//	R128SaveFPRegisters(save);
    R128SavePLLRegisters(save);
    R128SaveDDARegisters(save);
    R128SavePalette(save);

    save->dp_datatype      = INREG(R128_DP_DATATYPE);
    save->gen_reset_cntl   = INREG(R128_GEN_RESET_CNTL);
    save->clock_cntl_index = INREG(R128_CLOCK_CNTL_INDEX);
    save->amcgpio_en_reg   = INREG(R128_AMCGPIO_EN_REG);
    save->amcgpio_mask     = INREG(R128_AMCGPIO_MASK);
}

static void R128InitCommonRegisters(R128SavePtr save)
{
    save->ovr_clr            = 0;
    save->ovr_wid_left_right = 0;
    save->ovr_wid_top_bottom = 0;
    save->ov0_scale_cntl     = 0;
    save->mpp_tb_config      = 0;
    save->mpp_gp_config      = 0;
    save->subpic_cntl        = 0;
    save->viph_control       = 0;
    save->i2c_cntl_1         = 0;
    save->gen_int_cntl       = 0;
    save->cap0_trig_cntl     = 0;
    save->cap1_trig_cntl     = 0;
    save->mem_vga_wp_sel     = 0;
    save->mem_vga_rp_sel     = 0;
    save->bus_cntl           = BusCntl;
    /*
     * If bursts are enabled, turn on discards and aborts
     */
    if (save->bus_cntl & (R128_BUS_WRT_BURST|R128_BUS_READ_BURST))
	save->bus_cntl |= R128_BUS_RD_DISCARD_EN | R128_BUS_RD_ABORT_EN;
}

/* Define CRTC registers for requested video mode. */
static Bool R128InitCrtcRegisters(R128SavePtr save,
   				  ModeTiming *mode, ModeInfo *info)
{
    int    format;
    int    hsync_start;
    int    hsync_wid;
    int    hsync_fudge;
    int    vsync_wid;
    int    bytpp;
    int    hsync_fudge_default[] = { 0x00, 0x12, 0x09, 0x09, 0x06, 0x05 };
    int    hsync_fudge_fp[]      = { 0x12, 0x11, 0x09, 0x09, 0x05, 0x05 };
    int    hsync_fudge_fp_crt[]  = { 0x12, 0x10, 0x08, 0x08, 0x04, 0x04 };
    int    dac6bits;

    dac6bits=0;

    switch (info->bitsPerPixel) {
    case 4:  format = 1; bytpp = 0; dac6bits = 1; break;
    case 8:  format = 2; bytpp = 1; dac6bits = 1; break;
    case 16:
            if(info->greenWeight==5) 
               format = 3; else format = 4;
            bytpp = 2; 
            break;
    case 24: format = 5; bytpp = 3; break;      /*  RGB */
    case 32: format = 6; bytpp = 4; break;      /* xRGB */
    default:
	return 0;
    }
    
    if (HasPanelRegs)
	if (CRTOnly) hsync_fudge = hsync_fudge_fp_crt[format-1];
	else               hsync_fudge = hsync_fudge_fp[format-1];
    else
                           hsync_fudge = hsync_fudge_default[format-1];

    save->crtc_gen_cntl = (R128_CRTC_EXT_DISP_EN
			  | R128_CRTC_EN
			  | (format << 8)
			  | ((mode->flags & DOUBLESCAN)
			     ? R128_CRTC_DBL_SCAN_EN
			     : 0)
			  | ((mode->flags & INTERLACED)
			     ? R128_CRTC_INTERLACE_EN
			     : 0));

    save->crtc_ext_cntl = R128_VGA_ATI_LINEAR | R128_XCRT_CNT_EN | R128_VGA_MEM_PS_EN;
    save->dac_cntl      = (R128_DAC_MASK_ALL
			   | R128_DAC_VGA_ADR_EN
			   | (dac6bits ? 0 : R128_DAC_8BIT_EN));

    save->crtc_h_total_disp = ((((mode->CrtcHTotal / 8) - 1) & 0xffff)
			      | (((mode->CrtcHDisplay / 8) - 1) << 16));

    hsync_wid = (mode->CrtcHSyncEnd - mode->CrtcHSyncStart) / 8;
    if (!hsync_wid)       hsync_wid = 1;
    if (hsync_wid > 0x3f) hsync_wid = 0x3f;

    hsync_start = mode->CrtcHSyncStart - 8 + hsync_fudge;

    save->crtc_h_sync_strt_wid = ((hsync_start & 0xfff)
				 | (hsync_wid << 16)
				 | ((mode->flags & NHSYNC)
				    ? R128_CRTC_H_SYNC_POL
				    : 0));

#if 1
				/* This works for double scan mode. */
    save->crtc_v_total_disp = (((mode->CrtcVTotal - 1) & 0xffff)
			      | ((mode->CrtcVDisplay - 1) << 16));
#else
				/* This is what cce/nbmode.c example code
				   does -- is this correct? */
    save->crtc_v_total_disp = (((mode->CrtcVTotal - 1) & 0xffff)
			      | ((mode->CrtcVDisplay
				  * ((mode->Flags & DOUBLESCAN) ? 2 : 1) - 1)
				 << 16));
#endif

    vsync_wid = mode->CrtcVSyncEnd - mode->CrtcVSyncStart;
    if (!vsync_wid)       vsync_wid = 1;
    if (vsync_wid > 0x1f) vsync_wid = 0x1f;

    save->crtc_v_sync_strt_wid = (((mode->CrtcVSyncStart - 1) & 0xfff)
				 | (vsync_wid << 16)
				 | ((mode->flags & NVSYNC)
				    ? R128_CRTC_V_SYNC_POL
				    : 0));
    save->crtc_offset      = 0;
    save->crtc_offset_cntl = 0;
    save->crtc_pitch       = info->width / 8;

    save->config_cntl |= R128_CFG_VGA_RAM_EN;

#if 0
    /* Change the endianness of the aperture */
    switch (info->bitsPerPixel) {
    case 15:
    case 16: save->config_cntl |= APER_0_BIG_ENDIAN_16BPP_SWAP; break;
    case 32: save->config_cntl |= APER_0_BIG_ENDIAN_32BPP_SWAP; break;
    default: break;
    }
#endif

    return 1;
}

/* Define CRTC registers for requested video mode. */
static void R128InitFPRegisters(R128SavePtr orig, R128SavePtr save,
 				ModeTiming *mode, ModeInfo *info)
{
#if 0
    int   xres = mode->CrtcHDisplay;
    int   yres = mode->CrtcVDisplay;
    float Hratio, Vratio;

    if (CRTOnly) {
	save->crtc_ext_cntl  |= R128_CRTC_CRT_ON;
	save->crtc2_gen_cntl  = 0;
	save->fp_gen_cntl     = orig->fp_gen_cntl;
	save->fp_gen_cntl    &= ~(R128_FP_FPON |
				  R128_FP_CRTC_USE_SHADOW_VEND |
				  R128_FP_CRTC_HORZ_DIV2_EN |
				  R128_FP_CRTC_HOR_CRT_DIV2_DIS |
				  R128_FP_USE_SHADOW_EN);
	save->fp_gen_cntl    |= (R128_FP_SEL_CRTC2 |
				 R128_FP_CRTC_DONT_SHADOW_VPAR);
	save->fp_panel_cntl   = orig->fp_panel_cntl & ~R128_FP_DIGON;
	save->lvds_gen_cntl   = orig->lvds_gen_cntl & ~(R128_LVDS_ON |
							R128_LVDS_BLON);
	return;
    }


    if (xres > info->PanelXRes) xres = info->PanelXRes;
    if (yres > info->PanelYRes) yres = info->PanelYRes;

    Hratio = (float)xres/(float)info->PanelXRes;
    Vratio = (float)yres/(float)info->PanelYRes;

    save->fp_horz_stretch =
	(((((int)(Hratio * R128_HORZ_STRETCH_RATIO_MAX + 0.5))
	   & R128_HORZ_STRETCH_RATIO_MASK) << R128_HORZ_STRETCH_RATIO_SHIFT) |
	 (orig->fp_horz_stretch & (R128_HORZ_PANEL_SIZE |
				   R128_HORZ_FP_LOOP_STRETCH |
				   R128_HORZ_STRETCH_RESERVED)));
    save->fp_horz_stretch &= ~R128_HORZ_AUTO_RATIO_FIX_EN;
    if (Hratio == 1.0) save->fp_horz_stretch &= ~(R128_HORZ_STRETCH_BLEND |
						  R128_HORZ_STRETCH_ENABLE);
    else               save->fp_horz_stretch |=  (R128_HORZ_STRETCH_BLEND |
						  R128_HORZ_STRETCH_ENABLE);

    save->fp_vert_stretch =
	(((((int)(Vratio * R128_VERT_STRETCH_RATIO_MAX + 0.5))
	   & R128_VERT_STRETCH_RATIO_MASK) << R128_VERT_STRETCH_RATIO_SHIFT) |
	 (orig->fp_vert_stretch & (R128_VERT_PANEL_SIZE |
				   R128_VERT_STRETCH_RESERVED)));
    save->fp_vert_stretch &= ~R128_VERT_AUTO_RATIO_EN;
    if (Vratio == 1.0) save->fp_vert_stretch &= ~(R128_VERT_STRETCH_ENABLE |
						  R128_VERT_STRETCH_BLEND);
    else               save->fp_vert_stretch |=  (R128_VERT_STRETCH_ENABLE |
						  R128_VERT_STRETCH_BLEND);

    save->fp_gen_cntl = (orig->fp_gen_cntl & ~(R128_FP_SEL_CRTC2 |
					       R128_FP_CRTC_USE_SHADOW_VEND |
					       R128_FP_CRTC_HORZ_DIV2_EN |
					       R128_FP_CRTC_HOR_CRT_DIV2_DIS |
					       R128_FP_USE_SHADOW_EN));
    if (orig->fp_gen_cntl & R128_FP_DETECT_SENSE) {
	save->fp_gen_cntl |= (R128_FP_CRTC_DONT_SHADOW_VPAR |
			      R128_FP_TDMS_EN);
    }

    save->fp_panel_cntl        = orig->fp_panel_cntl;
    save->lvds_gen_cntl        = orig->lvds_gen_cntl;

    save->tmds_crc             = orig->tmds_crc;

    /* Disable CRT output by disabling CRT output and setting the CRT
       DAC to use CRTC2, which we set to 0's.  In the future, we will
       want to use the dual CRTC capabilities of the R128 to allow both
       the flat panel and external CRT to either simultaneously display
       the same image or display two different images. */
    save->crtc_ext_cntl  &= ~R128_CRTC_CRT_ON;
    save->dac_cntl       |= R128_DAC_CRT_SEL_CRTC2;
    save->crtc2_gen_cntl  = 0;

    /* WARNING: Be careful about turning on the flat panel */
#if 1
    save->lvds_gen_cntl  |= (R128_LVDS_ON | R128_LVDS_BLON);
#else
    save->fp_panel_cntl  |= (R128_FP_DIGON | R128_FP_BLON);
    save->fp_gen_cntl    |= (R128_FP_FPON);
#endif

    save->fp_crtc_h_total_disp = save->crtc_h_total_disp;
    save->fp_crtc_v_total_disp = save->crtc_v_total_disp;
    save->fp_h_sync_strt_wid   = save->crtc_h_sync_strt_wid;
    save->fp_v_sync_strt_wid   = save->crtc_v_sync_strt_wid;
#endif
}

/* Define PLL registers for requested video mode. */
static void R128InitPLLRegisters(R128SavePtr save, R128PLLPtr pll,
				 double dot_clock)
{
    unsigned long freq = dot_clock * 100;
    struct {
	int divider;
	int bitvalue;
    } *post_div,
      post_divs[]   = {
				/* From RAGE 128 VR/RAGE 128 GL Register
				   Reference Manual (Technical Reference
				   Manual P/N RRG-G04100-C Rev. 0.04), page
				   3-17 (PLL_DIV_[3:0]).  */
	{  1, 0 },              /* VCLK_SRC                 */
	{  2, 1 },              /* VCLK_SRC/2               */
	{  4, 2 },              /* VCLK_SRC/4               */
	{  8, 3 },              /* VCLK_SRC/8               */

	{  3, 4 },              /* VCLK_SRC/3               */
				/* bitvalue = 5 is reserved */
	{  6, 6 },              /* VCLK_SRC/6               */
	{ 12, 7 },              /* VCLK_SRC/12              */
	{  0, 0 }
    };

    if (freq > pll->max_pll_freq)      freq = pll->max_pll_freq;
    if (freq * 12 < pll->min_pll_freq) freq = pll->min_pll_freq / 12;

    for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
	save->pll_output_freq = post_div->divider * freq;
	if (save->pll_output_freq >= pll->min_pll_freq
	    && save->pll_output_freq <= pll->max_pll_freq) break;
    }

    save->dot_clock_freq = freq;
    save->feedback_div   = R128Div(pll->reference_div * save->pll_output_freq,
				   pll->reference_freq);
    save->post_div       = post_div->divider;

    save->ppll_ref_div   = pll->reference_div;
    save->ppll_div_3     = (save->feedback_div | (post_div->bitvalue << 16));
    save->htotal_cntl    = 0;
}

/* Define DDA registers for requested video mode. */
static Bool R128InitDDARegisters(R128SavePtr save,
				 R128PLLPtr pll, ModeInfo *info)
{
    int         DisplayFifoWidth = 128;
    int         DisplayFifoDepth = 32;
    int         XclkFreq;
    int         VclkFreq;
    int         XclksPerTransfer;
    int         XclksPerTransferPrecise;
    int         UseablePrecision;
    int         Roff;
    int         Ron;

    XclkFreq = pll->xclk;

    VclkFreq = R128Div(pll->reference_freq * save->feedback_div,
		       pll->reference_div * save->post_div);

    XclksPerTransfer = R128Div(XclkFreq * DisplayFifoWidth,
			       VclkFreq * (info->bytesPerPixel * 8));

    UseablePrecision = R128MinBits(XclksPerTransfer) + 1;

    XclksPerTransferPrecise = R128Div((XclkFreq * DisplayFifoWidth)
				      << (11 - UseablePrecision),
				      VclkFreq * (info->bytesPerPixel * 8));

    Roff  = XclksPerTransferPrecise * (DisplayFifoDepth - 4);

    Ron   = (4 * ram[r128_ramtype].MB
	     + 3 * (((ram[r128_ramtype].Trcd - 2)>0)?(ram[r128_ramtype].Trcd - 2):0)
	     + 2 * ram[r128_ramtype].Trp
	     + ram[r128_ramtype].Twr
	     + ram[r128_ramtype].CL
	     + ram[r128_ramtype].Tr2w
	     + XclksPerTransfer) << (11 - UseablePrecision);

    if (Ron + ram[r128_ramtype].Rloop >= Roff) {
	return 0;
    }

    save->dda_config = (XclksPerTransferPrecise
			| (UseablePrecision << 16)
			| (ram[r128_ramtype].Rloop << 20));

    save->dda_on_off = (Ron << 16) | Roff;

    return 1;
}


/* Define initial palette for requested video mode.  This doesn't do
   anything for XFree86 4.0. */
static void R128InitPalette(R128SavePtr save)
{
    int i;
    save->palette_valid = 1;
    for(i=0;i<256;i++) save->palette[i]=i | (i<<8) | (i<<16);
}

/* Define registers for a requested video mode. */
static Bool R128Init(ModeTiming *mode, ModeInfo *info, R128SavePtr save)
{
    double      dot_clock = mode->pixelClock/1000.0;

    R128InitCommonRegisters(save);
    if (!R128InitCrtcRegisters(save, mode, info)) return 0;
#if 0
    if (HasPanelRegs)
	R128InitFPRegisters(&info->SavedReg, save, mode, info);
#endif
    R128InitPLLRegisters(save, &pll, dot_clock);
    if (!R128InitDDARegisters(save, &pll, info))
	return 0;
//    if (!info->PaletteSavedOnVT) 
   		R128InitPalette(save);

    return 1;
}

static int r128_init(int, int, int);
static void r128_unlock(void);
static void r128_lock(void);

void __svgalib_skaccel_init(AccelSpecs * accelspecs, int bpp, int width_in_pixels);

static int r128_memory, r128_chiptype;
static int r128_is_linear, r128_linear_base, r128_mmio_base;

static CardSpecs *cardspecs;

static void r128_setpage(int page)
{
    page*=2;
    OUTREG(R128_MEM_VGA_WP_SEL, page | ((page+1)<<16));
    OUTREG(R128_MEM_VGA_RP_SEL, page | ((page+1)<<16));

}

static int __svgalib_r128_inlinearmode(void)
{
return r128_is_linear;
}

/* Fill in chipset specific mode information */

static void r128_getmodeinfo(int mode, vga_modeinfo *modeinfo)
{

    if(modeinfo->colors==16)return;

    modeinfo->maxpixels = r128_memory*1024/modeinfo->bytesperpixel;
    modeinfo->maxlogicalwidth = 4088;
    modeinfo->startaddressrange = r128_memory * 1024 - 1;
    modeinfo->haveblit = 0;
    modeinfo->flags &= ~HAVE_RWPAGE;

    if (modeinfo->bytesperpixel >= 1) {
	if(r128_linear_base)modeinfo->flags |= CAPABLE_LINEAR;
        if (__svgalib_r128_inlinearmode())
	    modeinfo->flags |= IS_LINEAR;
    }
}

/* Read and save chipset-specific registers */

static int r128_saveregs(unsigned char regs[])
{ 
  int i;

    r128_unlock();		
    R128SaveMode((R128SavePtr)(regs+VGA_TOTAL_REGS));
    return R128_TOTAL_REGS - VGA_TOTAL_REGS;
}

/* Set chipset-specific registers */

static void r128_setregs(const unsigned char regs[], int mode)
{  
    r128_unlock();		
    
    R128RestoreMode((R128SavePtr)(regs+VGA_TOTAL_REGS));

    R128Unblank();
}
/* Return nonzero if mode is available */

static int r128_modeavailable(int mode)
{
    struct info *info;
    ModeTiming *modetiming;
    ModeInfo *modeinfo;

    modeinfo = __svgalib_createModeInfoStructureForSvgalibMode(mode);

    if (IS_IN_STANDARD_VGA_DRIVER(mode))
	return __svgalib_vga_driverspecs.modeavailable(mode);

    info = &__svgalib_infotable[mode];
    if (r128_memory * 1024 < info->ydim * info->xbytes)
	return 0;

    modeinfo = __svgalib_createModeInfoStructureForSvgalibMode(mode);

    modetiming = malloc(sizeof(ModeTiming));
    if (__svgalib_getmodetiming(modetiming, modeinfo, cardspecs)) {
	free(modetiming);
	free(modeinfo);
	return 0;
    }
    free(modetiming);
    free(modeinfo);

    return SVGADRV;
}

/* Local, called by r128_setmode(). */

static void r128_initializemode(unsigned char *moderegs,
			    ModeTiming * modetiming, ModeInfo * modeinfo, int mode)
{ /* long k; */
    __svgalib_setup_VGA_registers(moderegs, modetiming, modeinfo);

    R128Init(modetiming, modeinfo, (R128SavePtr)(moderegs+VGA_TOTAL_REGS));

    return ;
}


static int r128_setmode(int mode, int prv_mode)
{
    unsigned char *moderegs;
    ModeTiming *modetiming;
    ModeInfo *modeinfo;

    if (IS_IN_STANDARD_VGA_DRIVER(mode)) {
	return __svgalib_vga_driverspecs.setmode(mode, prv_mode);
    }
    if (!r128_modeavailable(mode))
	return 1;

    modeinfo = __svgalib_createModeInfoStructureForSvgalibMode(mode);

    modetiming = malloc(sizeof(ModeTiming));
    if (__svgalib_getmodetiming(modetiming, modeinfo, cardspecs)) {
	free(modetiming);
	free(modeinfo);
	return 1;
    }

    moderegs = malloc(R128_TOTAL_REGS);

    r128_initializemode(moderegs, modetiming, modeinfo, mode);
    free(modetiming);

    __svgalib_setregs(moderegs);	/* Set standard regs. */
    r128_setregs(moderegs, mode);		/* Set extended regs. */
    free(moderegs);

    __svgalib_InitializeAcceleratorInterface(modeinfo);


    free(modeinfo);
    return 0;
}


/* Unlock chipset-specific registers */

static void r128_unlock(void)
{
    int vgaIOBase, temp;

    vgaIOBase = (inb(0x3CC) & 0x01) ? 0x3D0 : 0x3B0;
    outb(vgaIOBase + 4, 0x11);
    temp = inb(vgaIOBase + 5);
    outb(vgaIOBase + 5, temp & 0x7F);
    
}

static void r128_lock(void)
{
    int vgaIOBase, temp;

    vgaIOBase = (inb(0x3CC) & 0x01) ? 0x3D0 : 0x3B0;
    outb(vgaIOBase + 4, 0x11);
    temp = inb(vgaIOBase + 5);
    outb(vgaIOBase + 5, temp & 0x7F);   
}


#define VENDOR_ID 0x1002

/* Indentify chipset, initialize and return non-zero if detected */

static int r128_test(void)
{
    int found, id;
    unsigned long buf[64];
    
    found=__svgalib_pci_find_vendor_vga(VENDOR_ID,buf,0);

    if(found) return 0;
    id=(buf[0]>>16)&0xffff;
    if((id>=0x5350) || (id<=0x4fff)) return 0;
    
    r128_init(0,0,0);
    return 1;
}


/* Set display start address (not for 16 color modes) */
/* Cirrus supports any address in video memory (up to 2Mb) */

static void r128_setdisplaystart(int address)
{ 
  int naddress=address >> 2;
  __svgalib_outcrtc(0x0c,(naddress>>8)&0xff);
  __svgalib_outcrtc(0x0d,(naddress)&0xff);
  OUTREG(R128_CRTC_OFFSET, address);
}

/* Set logical scanline length (usually multiple of 8) */
/* Cirrus supports multiples of 8, up to 4088 */

static void r128_setlogicalwidth(int width)
{   
    int offset = width >> 3;
 
    if(CI.bytesperpixel>1) {
       offset = offset/CI.bytesperpixel;
    }
    __svgalib_outcrtc(0x13,offset&0xff);
    OUTREG(R128_CRTC_PITCH, offset);
}

static int r128_linear(int op, int param)
{
    if (op==LINEAR_ENABLE){r128_is_linear=1; return 0;};
    if (op==LINEAR_DISABLE){r128_is_linear=0; return 0;};
    if (op==LINEAR_QUERY_BASE) return r128_linear_base;
    if (op == LINEAR_QUERY_RANGE || op == LINEAR_QUERY_GRANULARITY) return 0;		/* No granularity or range. */
        else return -1;		/* Unknown function. */
}

static int r128_match_programmable_clock(int clock)
{
return clock ;
}

static int r128_map_clock(int bpp, int clock)
{
return clock ;
}

static int r128_map_horizontal_crtc(int bpp, int pixelclock, int htiming)
{
return htiming;
}

/* Function table (exported) */

DriverSpecs __svgalib_r128_driverspecs =
{
    r128_saveregs,
    r128_setregs,
    r128_unlock,
    r128_lock,
    r128_test,
    r128_init,
    r128_setpage,
    NULL,
    NULL,
    r128_setmode,
    r128_modeavailable,
    r128_setdisplaystart,
    r128_setlogicalwidth,
    r128_getmodeinfo,
    0,				/* old blit funcs */
    0,
    0,
    0,
    0,
    0,				/* ext_set */
    0,				/* accel */
    r128_linear,
    0,				/* accelspecs, filled in during init. */
    NULL,                       /* Emulation */
};

/* Initialize chipset (called after detection) */

static int r128_init(int force, int par1, int par2)
{
    unsigned long buf[64];
    int found=0;
    unsigned char *BIOS_POINTER;

    r128_unlock();
    if (force) {
	r128_memory = par1;
        r128_chiptype = par2;
    } else {

    };

    found=__svgalib_pci_find_vendor_vga(VENDOR_ID,buf,0);

    if(found) return 0;
    r128_chiptype=(buf[0]>>16)&0xffff;
    if((r128_chiptype>=0x5350) || (r128_chiptype<=0x4fff)) return 0;

    r128_linear_base=buf[4]&0xffffff00;
    r128_mmio_base=buf[6]&0xffffff00;

    MMIO_POINTER = mmap(0, 16*1024, PROT_READ | PROT_WRITE, MAP_SHARED, __svgalib_mem_fd,
       			r128_mmio_base);
    BIOS_POINTER = mmap(0, 64*1024, PROT_READ | PROT_WRITE, MAP_SHARED, __svgalib_mem_fd,
       			0xc0000);

    r128_memory      = INREG(R128_CONFIG_MEMSIZE) / 1024;
    BusCntl            = INREG(R128_BUS_CNTL);
    HasPanelRegs	= 0;
    CRTOnly		= 1;
    r128_ramtype	= 1;
#if 0
    pll.reference_freq = 2700;
    pll.min_pll_freq   = 12500;
    pll.max_pll_freq   = 40000;
    pll.reference_div =	INPLL(R128_PPLL_REF_DIV) & R128_PPLL_REF_DIV_MASK;
    pll.xclk           = 14000;
#else
#define R128_BIOS16(x) (*(unsigned short *)(BIOS_POINTER + x))
#define R128_BIOS32(x) (*(unsigned int *)(BIOS_POINTER + x))
    {
    CARD16 bios_header    = R128_BIOS16(0x48);
    CARD16 pll_info_block = R128_BIOS16(bios_header + 0x30);
    pll.reference_freq = R128_BIOS16(pll_info_block + 0x0e);
    pll.reference_div  = R128_BIOS16(pll_info_block + 0x10);
    pll.min_pll_freq   = R128_BIOS32(pll_info_block + 0x12);
    pll.max_pll_freq   = R128_BIOS32(pll_info_block + 0x16);
    pll.xclk           = R128_BIOS16(pll_info_block + 0x08);
#if 0
printf("pll: %i %i %i %i %i\n",pll.reference_freq,pll.reference_div,
    pll.min_pll_freq,    pll.max_pll_freq, pll.xclk);
#endif
    }
#endif

    munmap(MMIO_POINTER, 16*1024);
    munmap(BIOS_POINTER, 64*1024);
    
    if (__svgalib_driver_report) {
	printf("Using Rage 128 driver, %iKB.\n",r128_memory);
    };

    cardspecs = malloc(sizeof(CardSpecs));
    cardspecs->videoMemory = r128_memory;
    cardspecs->maxPixelClock4bpp = 75000;	
    cardspecs->maxPixelClock8bpp = 160000;	
    cardspecs->maxPixelClock16bpp = 160000;	
    cardspecs->maxPixelClock24bpp = 160000;
    cardspecs->maxPixelClock32bpp = 160000;
    cardspecs->flags = INTERLACE_DIVIDE_VERT | CLOCK_PROGRAMMABLE;
    cardspecs->maxHorizontalCrtc = 2040;
    cardspecs->maxPixelClock4bpp = 0;
    cardspecs->nClocks =0;
    cardspecs->mapClock = r128_map_clock;
    cardspecs->mapHorizontalCrtc = r128_map_horizontal_crtc;
    cardspecs->matchProgrammableClock=r128_match_programmable_clock;
    __svgalib_driverspecs = &__svgalib_r128_driverspecs;
    __svgalib_banked_mem_base=0xa0000;
    __svgalib_banked_mem_size=0x10000;
    __svgalib_linear_mem_base=r128_linear_base;
    __svgalib_linear_mem_size=r128_memory*0x400;
    __svgalib_mmio_base=r128_mmio_base;
    __svgalib_mmio_size=16*1024;
    return 0;
}
