Subversion Repositories shark

Rev

Blame | Last modification | View Log | RSS feed

/*
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 "vgapci.h"
#include "endianess.h"
#include "r128_reg.h"

#include <kernel/log.h>

static enum { Rage128=0, Radeon } chiptype; /* r128io needs to know */
static int dac6bits;
#include "r128io.h"

#ifdef __PPC
#define NO_BIOS
#else
#undef NO_BIOS
#endif

typedef int Bool;

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

typedef struct {
    uint16_t        reference_freq;
    uint16_t        reference_div;
    uint32_t        min_pll_freq;
    uint32_t        max_pll_freq;
    uint16_t        xclk;
} R128PLLRec, *R128PLLPtr;

typedef struct {
                                /* Common registers */
    uint32_t     ovr_clr;
    uint32_t     ovr_wid_left_right;
    uint32_t     ovr_wid_top_bottom;
    uint32_t     ov0_scale_cntl;
    uint32_t     mpp_tb_config;
    uint32_t     mpp_gp_config;
    uint32_t     subpic_cntl;
    uint32_t     viph_control;
    uint32_t     i2c_cntl_1;
    uint32_t     gen_int_cntl;
    uint32_t     cap0_trig_cntl;
    uint32_t     cap1_trig_cntl;
    uint32_t     bus_cntl;
    uint32_t     bus_cntl1;
    uint32_t     mem_cntl;
    uint32_t     config_cntl;
    uint32_t     mem_vga_wp_sel;
    uint32_t     mem_vga_rp_sel;
    uint32_t     surface_cntl;
    uint32_t     dac_cntl2;
    uint32_t     crtc_more_cntl;
    uint32_t     dac_ext_cntl;
    uint32_t     grph_buf_cntl;
    uint32_t     vga_buf_cntl;

                                /* Other registers to save for VT switches */
    uint32_t     dp_datatype;
    uint32_t     gen_reset_cntl;
    uint32_t     clock_cntl_index;
    uint32_t     amcgpio_en_reg;
    uint32_t     amcgpio_mask;
                                /* CRTC registers */
    uint32_t     crtc_gen_cntl;
    uint32_t     crtc_ext_cntl;
    uint32_t     dac_cntl;
    uint32_t     crtc_h_total_disp;
    uint32_t     crtc_h_sync_strt_wid;
    uint32_t     crtc_v_total_disp;
    uint32_t     crtc_v_sync_strt_wid;
    uint32_t     crtc_offset;
    uint32_t     crtc_offset_cntl;
    uint32_t     crtc_pitch;
                                /* CRTC2 registers */
    uint32_t     crtc2_gen_cntl;
                                /* Flat panel registers */
    uint32_t     fp_crtc_h_total_disp;
    uint32_t     fp_crtc_v_total_disp;
    uint32_t     fp_gen_cntl;
    uint32_t     fp_h_sync_strt_wid;
    uint32_t     fp_horz_stretch;
    uint32_t     fp_panel_cntl;
    uint32_t     fp_v_sync_strt_wid;
    uint32_t     fp_vert_stretch;
    uint32_t     lvds_gen_cntl;
    uint32_t     tmds_crc;
                                /* Computed values for PLL */
    uint32_t     dot_clock_freq;
    uint32_t     pll_output_freq;
    int        feedback_div;
    int        post_div;
                                /* PLL registers */
    uint32_t     ppll_ref_div;
    uint32_t     ppll_div_3;
    uint32_t     htotal_cntl;
                                /* DDA register */
    uint32_t     dda_config;
    uint32_t     dda_on_off;
    uint32_t     vga_dda_config;
    uint32_t     vga_dda_on_off;
                                /* Pallet */
    Bool       palette_valid;
    uint32_t     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" },
};

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

static void R128WaitForVerticalSync(void)
{
    volatile 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_BUS_CNTL1,            restore->bus_cntl1);
    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);

    if(chiptype == Rage128) {
        OUTREG(RADEON_SURFACE_CNTL,   restore->surface_cntl);
        OUTREG(RADEON_DAC_CNTL2,     restore->dac_cntl2);
        OUTREG(RADEON_CRTC_MORE_CNTL,restore->crtc_more_cntl);
        OUTREG(RADEON_DAC_EXT_CNTL,  restore->dac_ext_cntl);
        OUTREG(RADEON_GRPH_BUF_CNTL, restore->grph_buf_cntl);
        OUTREG(RADEON_VGA_BUF_CNTL,  restore->vga_buf_cntl);
    }

}

/* 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 */
#if 0
static void R128RestoreFPRegisters(R128SavePtr restore)
{
    uint32_t        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);
        }
    }
}
#endif
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 RADEONRestorePLLRegisters(R128SavePtr restore)
{
    OUTPLLP(R128_VCLK_ECP_CNTL,
            RADEON_VCLK_SRC_SEL_CPUCLK,
            ~(RADEON_VCLK_SRC_SEL_MASK));

    OUTPLLP(R128_PPLL_CNTL,
            R128_PPLL_RESET
            | R128_PPLL_ATOMIC_UPDATE_EN
            | R128_PPLL_VGA_ATOMIC_UPDATE_EN,
            ~(R128_PPLL_RESET
              | R128_PPLL_ATOMIC_UPDATE_EN
              | R128_PPLL_VGA_ATOMIC_UPDATE_EN));

    OUTREGP(R128_CLOCK_CNTL_INDEX,
            R128_PLL_DIV_SEL,
            ~(R128_PLL_DIV_SEL));

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

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

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

    R128PLLWriteUpdate();
    R128PLLWaitForReadUpdateComplete();

    OUTPLL(R128_HTOTAL_CNTL, restore->htotal_cntl);

    OUTPLLP(R128_PPLL_CNTL,
            0,
            ~(R128_PPLL_RESET
              | R128_PPLL_SLEEP
              | R128_PPLL_ATOMIC_UPDATE_EN
              | R128_PPLL_VGA_ATOMIC_UPDATE_EN));

    sleep(1); /* Let the clock to lock */

    OUTPLLP(R128_VCLK_ECP_CNTL,
            RADEON_VCLK_SRC_SEL_PPLLCLK,
            ~(RADEON_VCLK_SRC_SEL_MASK));
}

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_uint32_t(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);
#if 0 /* works without, and it causes problems with it */
    OUTREG(R128_GEN_RESET_CNTL,   restore->gen_reset_cntl);
#endif
    OUTREG(R128_DP_DATATYPE,      restore->dp_datatype);

    R128RestoreCommonRegisters( restore);
    R128RestoreCrtcRegisters( restore);
//    if (info->HasPanelRegs)
//      R128RestoreFPRegisters(restore);
//    if (!info->HasPanelRegs || info->CRTOnly)
    switch(chiptype) {
        case Rage128:
            R128RestorePLLRegisters(restore);
            break;
        case Radeon:
            RADEONRestorePLLRegisters(restore);
            break;
    }
           
    if(chiptype == Rage128) {
        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->bus_cntl1          = INREG(R128_BUS_CNTL1);
    save->mem_cntl           = INREG(R128_MEM_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);

    if(chiptype==Radeon) {
        save->surface_cntl   = INREG(RADEON_SURFACE_CNTL);
        save->dac_cntl2      = INREG(RADEON_DAC_CNTL2);
        save->crtc_more_cntl = INREG(RADEON_CRTC_MORE_CNTL);
        save->dac_ext_cntl   = INREG(RADEON_DAC_EXT_CNTL);
        save->grph_buf_cntl  = INREG(RADEON_GRPH_BUF_CNTL);
        save->vga_buf_cntl  =  INREG(RADEON_VGA_BUF_CNTL);
    }
}

/* 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);
}

#if 0
/* 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);
}
#endif

/* 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);
        if(chiptype == Rage128)
        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->config_cntl        = INREG(R128_CONFIG_CNTL);
    save->bus_cntl           = BusCntl;
    save->bus_cntl1          = INREG(R128_BUS_CNTL1);
    if(chiptype == Radeon) {
        if(save->bus_cntl & RADEON_BUS_READ_BURST)
            save->bus_cntl |=RADEON_BUS_RD_DISCARD_EN;
    }

    save->amcgpio_en_reg   = INREG(R128_AMCGPIO_EN_REG);
    save->amcgpio_mask     = INREG(R128_AMCGPIO_MASK);

    /*
     * 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 };

    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;
    if(chiptype == Radeon) save->crtc_ext_cntl |= R128_CRTC_CRT_ON;
    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(chiptype == Radeon) {
        save->crtc_pitch |= save->crtc_pitch<<16;
        save->surface_cntl = RADEON_SURF_TRANSLATION_DIS;

        save->dac_cntl2      = INREG(RADEON_DAC_CNTL2);
        save->crtc_more_cntl = INREG(RADEON_CRTC_MORE_CNTL);
        save->dac_ext_cntl   = INREG(RADEON_DAC_EXT_CNTL);
        save->grph_buf_cntl  = INREG(RADEON_GRPH_BUF_CNTL);
        save->vga_buf_cntl   =  INREG(RADEON_VGA_BUF_CNTL);

    }

#ifdef __PPC
    /* 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;
}

#if 0
/* 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
}
#endif

/* Define PLL registers for requested video mode. */
static void RADEONInitPLLRegisters(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               */
        { 16, 5 },
        {  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;
}

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
    switch(chiptype) {
        case Rage128:
            R128InitPLLRegisters(save, &pll, dot_clock);
            break;
        case Radeon:
            RADEONInitPLLRegisters(save, &pll, dot_clock);
            break;
    }
   
    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);

static int r128_memory;
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;
    modeinfo->flags |= HAVE_EXT_SET;

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

/* Read and save chipset-specific registers */

static int r128_saveregs(unsigned char regs[])
{
    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);

    free(modeinfo);
    return 0;
}


/* Unlock chipset-specific registers */
static void r128_unlock(void)
{
    __svgalib_outcrtc(0x11, __svgalib_incrtc(0x11) & 0x7f);    
}

static void r128_lock(void)
{
}

/* Indentify chipset, initialize and return non-zero if detected */
int r128_test(void)
{
    return 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;
}

static unsigned int cur_colors[16*2];

static int r128_cursor( int cmd, int p1, int p2, int p3, int p4, void *p5) {
    int i, j;
    unsigned long *b3;
    unsigned long l1, l2;
   
    switch(cmd){
        case CURSOR_INIT:
            return 1;
        case CURSOR_HIDE:
            OUTREG(R128_CRTC_GEN_CNTL,INREG(R128_CRTC_GEN_CNTL) & ~R128_CRTC_CUR_EN );
            break;
        case CURSOR_SHOW:
            OUTREG(R128_CRTC_GEN_CNTL,INREG(R128_CRTC_GEN_CNTL) | R128_CRTC_CUR_EN );
            break;
        case CURSOR_POSITION:
            OUTREG(R128_CUR_HORZ_VERT_OFF, R128_CUR_LOCK | 0);
            OUTREG(R128_CUR_HORZ_VERT_POSN, p2|(p1<<16));
            break;
        case CURSOR_SELECT:
            i=r128_memory*1024-(p1+1)*4096;
            OUTREG(R128_CUR_OFFSET,i);
            OUTREG(R128_CUR_CLR0,cur_colors[p1*2]);
            OUTREG(R128_CUR_CLR1,cur_colors[p1*2+1]);
            break;
        case CURSOR_IMAGE:
            i=r128_memory*1024-(p1+1)*4096;
            b3=(unsigned long *)p5;
            switch(p2) {
                case 0:
                    cur_colors[p1*2]=p3;
                    cur_colors[p1*2+1]=p4;

                    for(j=0;j<32;j++) {
                        l1=*(b3+j); /* source */
                        l2=*(b3+32+j); /* mask */
                        l1=BE32(l1);
                        l2=BE32(l2);
                        *(unsigned long *)(LINEAR_POINTER+i+16*j)=~l2;
                        *(unsigned long *)(LINEAR_POINTER+i+16*j+4)=0xffffffff;
                        *(unsigned long *)(LINEAR_POINTER+i+16*j+8)=l1&l2;
                        *(unsigned long *)(LINEAR_POINTER+i+16*j+12)=0;
                        *(unsigned long *)(LINEAR_POINTER+i+512+16*j)=0xffffffff;
                        *(unsigned long *)(LINEAR_POINTER+i+512+16*j+4)=0xffffffff;
                        *(unsigned long *)(LINEAR_POINTER+i+512+16*j+8)=0;
                        *(unsigned long *)(LINEAR_POINTER+i+512+16*j+12)=0;
                    }
                break;
            }
            break;
    }
    return 0;
}      

static Emulation r128_emul =
{
        0,
        0,
        0,
        0,
        0,
        0,
        0,                              /* screenoff */
        0,                              /* screenon */
        R128WaitForVerticalSync,        /* waitretrace */
};

static int r128_ext_set( unsigned what, va_list params )
{
    int param2;
    param2 = va_arg(params, int);

    switch (what) {
        case VGA_EXT_AVAILABLE:
            switch (param2) {
                case VGA_AVAIL_SET:
                    return VGA_EXT_AVAILABLE | VGA_EXT_SET | VGA_EXT_CLEAR | VGA_EXT_RESET;
                case VGA_AVAIL_ACCEL:
                    return 0;
                case VGA_AVAIL_FLAGS:
                    return VGA_CLUT8;
            }
            break;
        case VGA_EXT_SET:
            if (param2 & VGA_CLUT8) OUTREGP( R128_DAC_CNTL, R128_DAC_8BIT_EN, R128_DAC_MASK_ALL );
            return 1;
            break;
    }
    return 0;
}

/* 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,
    r128_ext_set,               /* ext_set */
    0,                          /* accel */
    r128_linear,
    0,                          /* accelspecs, filled in during init. */
    &r128_emul,                 /* Emulation */
    r128_cursor,
};

#define VENDOR_ID 0x1002

/* Initialize chipset (called after detection) */
static int r128_init(int force, int par1, int par2)
{
    unsigned long buf[64];
    int found=0, id, NOBIOS=0;
    unsigned char *BIOS_POINTER;
    char chipnames[2][9]={"Rage 128", "Radeon"};

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

    };

    found=__svgalib_pci_find_vendor_vga(VENDOR_ID,buf,0);

    if(found) return 0;
   
    chiptype=-1;
    id=(buf[0]>>16)&0xffff;
   
    if( (id==0x4c45) ||
        (id==0x4c56) ||
        (id==0x4d46) ||
        (id==0x4d4c) ||
        ((id>>8)==0x50) ||
        ((id>>8)==0x52) ||
        ((id>>8)==0x53) ||
        ((id>>8)==0x54))
        chiptype=Rage128;
       
    if( (id==0x4242) ||
        (id==0x4c57) ||
        (id==0x4c59) ||
        (id==0x4c5a) ||
        ((id>>8)==0x51))
        chiptype = Radeon;
       
    if(chiptype==-1) return 0;
   
    r128_linear_base=buf[4]&0xffffff00;
    r128_mmio_base=buf[6]&0xffffff00;

    MMIO_POINTER = (void *)r128_mmio_base;

    r128_memory      = INREG(R128_CONFIG_MEMSIZE) / 1024;
    BusCntl            = INREG(R128_BUS_CNTL);
    HasPanelRegs        = 0;
    CRTOnly             = 1;
    r128_ramtype        = 1;

#ifndef __PPC    
    if(__svgalib_secondary)
#endif
        NOBIOS=1;

    if(NOBIOS) {
        int x_mpll_ref_fb_div, xclk_cntl, Nx, M;
        int PostDivSet[] = {0, 1, 2, 4, 8, 3, 6, 12};
   
        switch(chiptype) {
            case Rage128:
                pll.reference_freq = 2950;
                pll.min_pll_freq   = 12500;
                pll.max_pll_freq   = 25000;
                pll.reference_div =     INPLL(R128_PPLL_REF_DIV) & R128_PPLL_REF_DIV_MASK;
   
                x_mpll_ref_fb_div = INPLL(R128_X_MPLL_REF_FB_DIV);
                xclk_cntl = INPLL(R128_XCLK_CNTL) & 0x7;
                Nx = (x_mpll_ref_fb_div & 0x00FF00) >> 8;
                M =  (x_mpll_ref_fb_div & 0x0000FF);
                pll.xclk =  R128Div((2 * Nx * pll.reference_freq),
                                      (M * PostDivSet[xclk_cntl]));
                break;
            case Radeon:
                pll.reference_freq = 2700;
                pll.min_pll_freq   = 12500;
                pll.max_pll_freq   = 35000;
                pll.reference_div  = 67;
                pll.xclk           = 16667;
                break;
        }
    } else
#define R128_BIOS16(x) (*(unsigned short *)(BIOS_POINTER + x))
#define R128_BIOS32(x) (*(unsigned int *)(BIOS_POINTER + x))
    {
        uint16_t bios_header;
        uint16_t pll_info_block;
        BIOS_POINTER = (void *)0xc0000;
        bios_header    = R128_BIOS16(0x48);
        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 1
printk(KERN_INFO "pll: %i %i %i %i %i\n",pll.reference_freq,pll.reference_div,
    pll.min_pll_freq,    pll.max_pll_freq, pll.xclk);
#endif

    r128_mapio();
       
    if (__svgalib_driver_report) {
        printk(KERN_INFO "Using ATI %s driver, %iKB.\n",chipnames[chiptype],r128_memory);
    };
   
        __svgalib_modeinfo_linearset |= IS_LINEAR;
       
    cardspecs = malloc(sizeof(CardSpecs));
    cardspecs->videoMemory = r128_memory;
    cardspecs->maxPixelClock4bpp = 75000;      
    cardspecs->maxPixelClock8bpp = 250000;     
    cardspecs->maxPixelClock16bpp = 250000;    
    cardspecs->maxPixelClock24bpp = 250000;
    cardspecs->maxPixelClock32bpp = 250000;
    cardspecs->flags = INTERLACE_DIVIDE_VERT | CLOCK_PROGRAMMABLE;
    cardspecs->maxHorizontalCrtc = 4080;
    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;
    if(chiptype==Radeon) {
        __svgalib_banked_mem_base=r128_linear_base;
        __svgalib_r128_driverspecs.__svgalib_setpage = __svgalib_emul_setpage;
    }

    sleep(4);
   
    return 1;
}