/*	$Id: init_ati.c,v 1.1.1.1 2006/08/23 17:03:06 pefo Exp $	*/
/*
 * Copyright (c) 2005 Opsycon AB  (www.opsycon.se)
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Opsycon AB, Sweden.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

/*
 *  Initializes the ATI Rage XL chip for console usage.
 */
#define LOGOSPLASH

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/malloc.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>

#include <pmon.h>
#include <target/init_ati.h>

#include "wsfont.h"
//#include "vt220l8x8.h"
#include "vt220l8x10.h"

#ifdef LOGOSPLASH
#include "tigersplash.h"
#endif

struct ati_info {
	int	sub_id;
	int	refclk;
	int	mem_type;
	int	mem_size;
	int	magic;
	int	mem_cntl;
	int	ext_mem_cntl;
	int	mem_addr_conf;
	int	bus_cntl;
	int	dac_cntl;
	int	hw_debug;
	int	macro_cntl;
	int8_t	dll2_cntl;
	int8_t	yclk_cntl;
	int	pll;
	int	mclk;
	int	rfsh;
};

struct ati_map {
	paddr_t	ati_ctrl_pbase;
	vaddr_t	ati_ctrl_vbase;
	size_t	ati_vid_size;
	paddr_t	ati_vid_pbase;
	vaddr_t	ati_vid_vbase;
	struct wsdisplay_font *font;
};

struct ati_pll {
	int	refclk_period;
	int	pll_period;
	int	mclk_period;
	int	ref_div;
	int	ext_cntl;
	int	vclk_cntl;
	int	mclk_post_div_real;
	int	mclk_fb_div;
	int	vclk_post_div_real;
	int	vclk_post_div;
	int	vclk_fb_div;
	int	gen_cntl;
	int	dsp_on_off;
	int	dsp_config;
};

struct ati_scan {
	int	h_tot_disp;
	int	h_sync_strt_wid;
	int	v_tot_disp;
	int	v_sync_strt_wid;
	int	off_pitch;
	int	gen_cntl;
	int	dp_pix_width;
	int	dp_chain_mask;
};

struct screeninfo {
	int	xres;		/* visible resolution           */
	int	yres;
	int	bits_per_pixel;

	int	pixclock;	/* pixel clock in kHz		*/
        /* Timing: All values in pixclocks */
	int	left_margin;	/* time from sync to picture    */
	int	right_margin;	/* time from picture to sync    */
	int	upper_margin;	/* time from sync to picture    */
	int	lower_margin;
	int	hsync_len;	/* length of horizontal sync    */
	int	vsync_len;	/* length of vertical sync      */
};

struct palette {
	u_int8_t r, g, b;
};

struct ati_config {
	struct ati_info chip;
	struct ati_map map;
	struct ati_pll pll;
	struct ati_scan scan;
	struct screeninfo vpar;
	struct palette *palette;
	int	splashoffs;
	int	splashxsize;
	int	splashysize;
};

#define	MB	(1024*1024)
#define	SDRAM	4
#define	SGRAM	5
#define	SDRAM32	6

#define	ROUND(x, y)	(((x) + ((y) - 1)) & ~((y) - 1))

#define	MAGIC_SDRAM_PLL	0x00008000
#define	GTB_DSP		0x00020000
#define	XL_DLL		0x00040000

struct ati_config ati_config;
int ati_config_done;

int ati_var_to_pll(struct ati_config *);
void ati_set_pll(struct ati_config *);

#ifdef ATI_DEBUG
void ati_dump_pll(struct ati_config *);
static void aty_xl_powerup(struct ati_config *config);
#endif

struct ati_info chip_configs[] = {
	{	0x0000, 14318, SDRAM32, 8*MB,
		GTB_DSP | MAGIC_SDRAM_PLL | XL_DLL,
		0x00065a2b, 0xe0000cf1, 0x00200213, 0x7333a001,
                0x8000000a, 0x48833800, 0x007f0779,
                0x10, 0x19,
		230000, 50000
/*
M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | M64F_EXTRA_BRIGHT 
*/
	}
};

struct screeninfo default_vpar = {
    /* Standard VGA 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
    640, 480, 8,
    25175, 48, 16, 33, 10, 96, 2,
};

struct screeninfo m1280x1024_vpar = {
    1280, 1024, 8,
    110000, 48, 16, 33, 10, 96, 2,
};


#define NCONFIGS (sizeof chip_configs / sizeof(struct ati_info))

static const int lcd_tbl[] = {
        0x01, 0x000520C0,
        0x08, 0x02000408,
        0x03, 0x00000F00,
        0x00, 0x00000000,
        0x02, 0x00000000,
        0x04, 0x00000000,
        0x05, 0x00000000,
        0x06, 0x00000000,
        0x33, 0x00000000,
        0x34, 0x00000000,
        0x35, 0x00000000,
        0x36, 0x00000000,
        0x37, 0x00000000,
	-1
};

static int rfsh_freq[] = {
	50, 66, 75, 83, 90, 95, 100, 105, 110, 115, 120, 125, 133, 143, 166
};


static inline int
ati_offs(int offs)
{
	if (offs >= 0x400)
		return offs - 0x800;
	else
		return offs;
}

static inline void
ati_wr_8(vaddr_t ati_base, int offs, u_int8_t data)
{
	out8(ati_base + ati_offs(offs), data);
}

static inline u_int8_t
ati_rd_8(vaddr_t ati_base, int offs)
{
	return in8(ati_base + ati_offs(offs));
}

static inline u_int32_t
ati_rd_32(vaddr_t ati_base, int offs)
{
	return in32(ati_base + ati_offs(offs));
}

static inline void
ati_wr_16(vaddr_t ati_base, int offs, u_int16_t data)
{
	out16(ati_base + ati_offs(offs), htole16(data));
}

static inline void
ati_wr_32(vaddr_t ati_base, int offs, u_int32_t data)
{
	out32(ati_base + ati_offs(offs), htole32(data));
}

static void
ati_wr_pll(vaddr_t ati_base, int offs, u_int8_t data)
{
	offs <<= 2;
	ati_wr_8(ati_base, CLOCK_CNTL_ADDR, PLL_WR_EN | offs);
	ati_wr_8(ati_base, CLOCK_CNTL_DATA, data);
	ati_wr_8(ati_base, CLOCK_CNTL_ADDR, offs);
}

static u_int8_t
ati_rd_pll(vaddr_t ati_base, int offs)
{
	offs <<= 2;
	ati_wr_8(ati_base, CLOCK_CNTL_ADDR, offs);
	return ati_rd_8(ati_base, CLOCK_CNTL_DATA);
}

static void
ati_set_color(struct ati_config *config, int index, char r, char g, char b)
{
	vaddr_t ati_base = config->map.ati_ctrl_vbase;
	struct palette *palette = config->palette;

	palette[index].r = r;
	palette[index].g = g;
	palette[index].b = b;
	ati_wr_8(ati_base, DAC_W_INDEX, index);
	ati_wr_8(ati_base, DAC_DATA, r);
	ati_wr_8(ati_base, DAC_DATA, g);
	ati_wr_8(ati_base, DAC_DATA, b);
}

static void
ati_set_palette(struct ati_config *config)
{
	vaddr_t ati_base = config->map.ati_ctrl_vbase;
	struct palette *palette;
	int i;

	if (config->palette == NULL) {
		palette = malloc(sizeof *palette * 256, M_DEVBUF, M_WAITOK);
		config->palette = palette;
	}
	for (i = 0; i <= 255; i++)
		ati_set_color(config, i, 255 * (i & 1),
		    255 * ((i >> 1) & 1), 255 * ((i >> 2) & 1));
}

static void
ati_set_scan(struct ati_config *config)
{
	int xres, yres;
	int htotal, hdisp, h_sync_strt, h_sync_dly, h_sync_wid;
	int vtotal, vdisp, v_sync_strt, v_sync_wid;
	int c_sync, pix_width;
	int sync_pol;
	vaddr_t ati_base = ati_config.map.ati_ctrl_vbase;

	sync_pol = 1;
	xres = ROUND(config->vpar.xres, 8);
	yres = ROUND(config->vpar.yres, 8);
	hdisp = xres / 8 - 1;
	h_sync_strt = hdisp + (config->vpar.right_margin / 8);
	h_sync_dly = config->vpar.right_margin & 7;
	h_sync_wid = (config->vpar.hsync_len + 7) / 8;
	config->scan.h_tot_disp = h_sync_strt + h_sync_wid + (h_sync_dly +
	    config->vpar.left_margin + 7) / 8 | (hdisp << 16);
	config->scan.h_sync_strt_wid = (sync_pol << 21) | /* Sync polarity */
	    (h_sync_strt & 0xff) | (h_sync_dly << 8) |
	    ((h_sync_strt & 0x100) << 4) | (h_sync_wid << 16);

	vdisp = yres - 1;
	v_sync_strt = vdisp + config->vpar.lower_margin;
	v_sync_wid = config->vpar.vsync_len;
	config->scan.v_tot_disp = v_sync_strt + v_sync_wid +
	    config->vpar.upper_margin | (vdisp << 16);
	config->scan.v_sync_strt_wid = (sync_pol << 21) | /* Sync polarity */
	    v_sync_strt | (v_sync_wid << 16);

	c_sync = 0;

	switch (config->vpar.bits_per_pixel) {
	default:
	case 8:
		pix_width = CRTC_PIX_WIDTH_8BPP;
		config->scan.dp_pix_width = BYTE_ORDER_LSB_TO_MSB |
		    HOST_8BPP | SRC_8BPP | DST_8BPP;
		config->scan.dp_chain_mask = 0x8080;
		break;
	case 15:
	case 16:
		pix_width = CRTC_PIX_WIDTH_15BPP;
		config->scan.dp_pix_width = BYTE_ORDER_LSB_TO_MSB |
		    HOST_15BPP | SRC_15BPP | DST_15BPP;
		config->scan.dp_chain_mask = 0x4210;
		break;
	case 24:
		pix_width = CRTC_PIX_WIDTH_24BPP;
		config->scan.dp_pix_width = BYTE_ORDER_LSB_TO_MSB |
		    HOST_8BPP | SRC_8BPP | DST_8BPP;
		config->scan.dp_chain_mask = 0x8080;
		break;
	case 32:
		pix_width = CRTC_PIX_WIDTH_32BPP;
		config->scan.dp_pix_width = BYTE_ORDER_LSB_TO_MSB |
		    HOST_32BPP | SRC_32BPP | DST_32BPP;
		config->scan.dp_chain_mask = 0x8080;
		break;
	}
	
	/* Set pitch, no virtual size (yet?) so use real */
	config->scan.off_pitch = xres << 19;

	config->scan.gen_cntl = CRTC_EXT_DISP_EN | CRTC_ENABLE |
	    pix_width | c_sync;

	ati_wr_32(ati_base, CRTC_H_TOTAL_DISP, config->scan.h_tot_disp);
	ati_wr_32(ati_base, CRTC_H_SYNC_STRT_WID, config->scan.h_sync_strt_wid);
	ati_wr_32(ati_base, CRTC_V_TOTAL_DISP, config->scan.v_tot_disp);
	ati_wr_32(ati_base, CRTC_V_SYNC_STRT_WID, config->scan.v_sync_strt_wid);
	ati_wr_32(ati_base, CRTC_VLINE_CRNT_VLINE, 0);
	ati_wr_32(ati_base, CRTC_OFF_PITCH, config->scan.off_pitch);
	ati_wr_32(ati_base, CRTC_GEN_CNTL, config->scan.gen_cntl);
}

static void
ati_set_par(struct ati_config *config)
{
	vaddr_t ati_base = ati_config.map.ati_ctrl_vbase;
	int mdata, data;

	ati_set_scan(config);
	/* Set clocks */
	ati_set_pll(&ati_config);

	mdata = ati_rd_32(ati_base, MEM_CNTL);
	mdata |= config->chip.rfsh << 20;
	switch (config->vpar.bits_per_pixel) {
	case 8:
	case 24:
	default:
		break;
	case 16:
		mdata |= 0x04 << 24;
		break;
	case 32:
		mdata |= 0x08 << 24;
		break;
	}

	/* For GT */
	ati_wr_32(ati_base, DAC_CNTL, 0x86010102);
	ati_wr_32(ati_base, BUS_CNTL, 0x7b23a040);
//	data = ati_rd_32(ati_base, EXT_MEM_CNTL) | 0x5000001;
//	ati_wr_32(ati_base, EXT_MEM_CNTL, data);

	/* Update mem */
	ati_wr_32(ati_base, MEM_CNTL, mdata);

	ati_wr_8(ati_base, DAC_MASK, 0xff);
}

static void
ati_reset_clocks(struct ati_config *config, int hsync)
{
	vaddr_t ati_base = ati_config.map.ati_ctrl_vbase;
	int data;

	ati_wr_8(ati_base, GEN_TEST_CNTL + 1, 0x01);
	ati_wr_8(ati_base, GEN_TEST_CNTL + 1, 0x00);
	ati_wr_8(ati_base, GEN_TEST_CNTL + 1, 0x02);
	delay(5000);

	ati_wr_pll(ati_base, MCLK_FB_DIV, config->pll.mclk_fb_div);
	delay(15000);

	data = ati_rd_pll(ati_base, PLL_GEN_CNTL) & 0x7f;
	ati_wr_pll(ati_base, PLL_GEN_CNTL, data);

	ati_wr_pll(ati_base, DLL_CNTL, 0x82);
	ati_wr_pll(ati_base, DLL_CNTL, 0xe2);
	delay(5000);
	ati_wr_pll(ati_base, DLL_CNTL, 0x82);
	delay(6000);

	ati_wr_8(ati_base, GEN_TEST_CNTL + 1, 0x00);
	delay(5000);
	ati_wr_8(ati_base, GEN_TEST_CNTL + 3, 0x04);
	delay(6000);

	/* Reset SDRAM */
	data = ati_rd_8(ati_base, EXT_MEM_CNTL) | 0x02;
	ati_wr_8(ati_base, EXT_MEM_CNTL, data);
	data |= 0x08;
	ati_wr_8(ati_base, EXT_MEM_CNTL, data);
	data |= 0x0c;
	ati_wr_8(ati_base, EXT_MEM_CNTL, data);
	delay(5000);
	data &= 0xf3;
	ati_wr_8(ati_base, EXT_MEM_CNTL, data);
	data &= 0xfd;
	ati_wr_8(ati_base, EXT_MEM_CNTL, data);
	delay(5000);

	ati_wr_8(ati_base, CRTC_GEN_CNTL + 3, hsync ? 0 : 4);

	ati_wr_pll(ati_base, SPLL_CNTL2, 0x10);
	ati_wr_pll(ati_base, PLL_GEN_CNTL, config->pll.gen_cntl);
	ati_wr_pll(ati_base, PLL_VCLK_CNTL, config->pll.vclk_cntl);
	delay(10000);
}

#ifdef LOGOSPLASH
static void
ati_splash(struct ati_config *config)
{
	vaddr_t ati_base = ati_config.map.ati_ctrl_vbase;
	int splashoffs;

	char **nextline;
	char *xpmline;
	char *dspline;
	char *args[4];
	int xsize, ysize, colors, depth;
	int index, rgb;
	int i, j, xoffs, yoffs;

	splashoffs = config->vpar.xres * config->vpar.yres;
	nextline = splashimage;
	xpmline = *nextline++;
	if (argvize(args, xpmline) != 4)
		return;
	xsize = atoi(args[0]);
	ysize = atoi(args[1]);
	config->splashxsize = xsize;
	config->splashysize = ysize;
	config->splashoffs = splashoffs;
	colors = atoi(args[2]);
	depth = atoi(args[3]);
	if (depth != 1)
		return;
	for (i = 0; i < colors; i++) {
		xpmline = *nextline++;
		index = xpmline[0];
		gethex(&rgb, &xpmline[5], 6); 
		ati_set_color(config, index, (rgb >> 16) & 0xff,
		    (rgb >> 8) & 0xff, rgb & 0xff);
	}
	xoffs = (config->vpar.xres - xsize) / 2;
	yoffs = config->vpar.yres - ysize / 2;
	for (i = 0; i < ysize; i++) {
		xpmline = *nextline++;
		dspline = (u_char *)config->map.ati_vid_vbase + splashoffs;
		dspline += xoffs + (i + yoffs) * config->vpar.xres;
		for (j = 0; j < xsize; j+=2, xpmline+=2) {
			*dspline++ = xpmline[1];
			*dspline++ = xpmline[0];
		}
	}
	splashoffs += config->vpar.xres * config->vpar.yres / 2;
	ati_wr_32(ati_base, CRTC_OFF_PITCH,
	    config->scan.off_pitch | splashoffs / 8);
	delay(5000000);
}
static void
ati_splash_saver(struct ati_config *config)
{
	vaddr_t ati_base = ati_config.map.ati_ctrl_vbase;
	int splashoffs;
	int xoffs, yoffs, max_xoffs, max_yoffs;

	max_xoffs = config->vpar.xres - config->splashxsize;
	xoffs = max_xoffs / 2;
	max_yoffs = config->vpar.yres - config->splashysize;
	yoffs = config->vpar.yres - config->splashysize / 2;

	splashoffs = config->splashoffs;
	splashoffs += xoffs + yoffs * config->vpar.xres;
	splashoffs -= max_yoffs * rand() / 32768 * config->vpar.xres;
	splashoffs -= max_xoffs * rand() / 32768;
	ati_wr_32(ati_base, CRTC_OFF_PITCH,
	    config->scan.off_pitch | splashoffs / 8);
}
#endif

void
init_ati()
{
	pcireg_t pdata;
	pcitag_t tag;
	paddr_t xl_vid_base;
	paddr_t xl_phys_base;
	vaddr_t xl_virt_base;
	struct ati_info *info;
	int i, reg, error;

	if (ati_config_done)
		return;

	tag = _pci_make_tag(0, 17, 0);
	pdata = _pci_conf_read(tag, PCI_ID_REG);
	if ((PCI_VENDOR(pdata) != PCI_VENDOR_ATI) ||
	    (PCI_PRODUCT(pdata) != PCI_PRODUCT_ATI_RAGEXL))
		return;

	pdata = _pci_conf_read(tag, PCI_SUBVEND_0) & 0xffff;

	info = NULL;
	for (i = 0; i < NCONFIGS; i++) {
		if (chip_configs[i].sub_id == pdata)
			info = &chip_configs[i];
	}

	if (info == NULL)
		return;

	ati_config.chip = *info;
	ati_config.vpar = default_vpar;

	for (reg = PCI_MAPREG_START; reg <= PCI_MAPREG_PCB_END; reg += 4) {
		pdata = _pci_conf_read(tag, reg);
		if (PCI_MAPREG_TYPE(pdata) == PCI_MAPREG_TYPE_MEM)
			break;
	}
	if (PCI_MAPREG_TYPE(pdata) != PCI_MAPREG_TYPE_MEM)
		return;

	/* Looks like we have an ATI Rage XL. Start with mapping */
	xl_vid_base = PCI_MAPREG_MEM_ADDR(pdata);
	xl_phys_base = xl_vid_base + 0x7ffc00;
	xl_virt_base = PHYS_TO_UNCACHED(xl_phys_base);
	ati_config.map.ati_ctrl_vbase = xl_virt_base;
	ati_config.map.ati_ctrl_pbase = xl_phys_base;

	ati_config.map.ati_vid_pbase = xl_vid_base;
	xl_vid_base = PHYS_TO_UNCACHED(xl_vid_base);
	ati_config.map.ati_vid_vbase = xl_vid_base;
	ati_config.map.ati_vid_size = 0x800000-0x1000;	/* XXX Vram size  - 4k*/
	ati_config.map.ati_vid_size = 0x800000-0x1000;	/* XXX Vram size  - 4k*/
	
	/* Refresh rate */
	for (i = 0;; i++) {
		if (ati_config.chip.mclk < rfsh_freq[i]) {
			ati_config.chip.rfsh = i;
			break;
		}
	}

#if 0
	aty_xl_powerup(&ati_config);

#else
	ati_wr_8(xl_virt_base, CONFIG_STAT0, 0x86);
	delay(10000);

//	compute pll data
	if (ati_var_to_pll(&ati_config) != 0)
		return;

	ati_wr_pll(xl_virt_base, LVDS_CNTL0, 0);
	ati_wr_pll(xl_virt_base, DLL2_CNTL, ati_config.chip.dll2_cntl);
	ati_wr_pll(xl_virt_base, V2PLL_CNTL, 0x10);
	ati_wr_pll(xl_virt_base, MPLL_CNTL, MPLL_GAIN);
	ati_wr_pll(xl_virt_base, VPLL_CNTL, VPLL_GAIN);
	ati_wr_pll(xl_virt_base, PLL_VCLK_CNTL, 0);
	ati_wr_pll(xl_virt_base, VFC_CNTL, 0x1b);
	ati_wr_pll(xl_virt_base, PLL_REF_DIV, ati_config.pll.ref_div);
	ati_wr_pll(xl_virt_base, PLL_EXT_CNTL, ati_config.pll.ext_cntl);
	ati_wr_pll(xl_virt_base, SPLL_CNTL2, 3);
	ati_wr_pll(xl_virt_base, PLL_GEN_CNTL, 0x44);

	ati_reset_clocks(&ati_config, 0);
        delay(10000);

	ati_wr_pll(xl_virt_base, VCLK_POST_DIV, 0x03);
        ati_wr_pll(xl_virt_base, VCLK0_FB_DIV, 0xDA);
        ati_wr_pll(xl_virt_base, VCLK_POST_DIV, 0x0F);
        ati_wr_pll(xl_virt_base, VCLK1_FB_DIV, 0xF5);
        ati_wr_pll(xl_virt_base, VCLK_POST_DIV, 0x3F);
        ati_wr_pll(xl_virt_base, PLL_EXT_CNTL, 0x40 | ati_config.pll.ext_cntl);
        ati_wr_pll(xl_virt_base, VCLK2_FB_DIV, 0x00);
        ati_wr_pll(xl_virt_base, VCLK_POST_DIV, 0xFF);
        ati_wr_pll(xl_virt_base, PLL_EXT_CNTL, 0xC0 | ati_config.pll.ext_cntl);
        ati_wr_pll(xl_virt_base, VCLK3_FB_DIV, 0x00);

        ati_wr_8(xl_virt_base, BUS_CNTL, 0x01);
        ati_wr_32(xl_virt_base, BUS_CNTL, ati_config.chip.bus_cntl | (1 << 27));

        ati_wr_32(xl_virt_base, CRTC_GEN_CNTL, 0x04000200);
        ati_wr_16(xl_virt_base, CONFIG_STAT0, 0x0020);
        ati_wr_32(xl_virt_base, MEM_CNTL, 0x10151A33);
        ati_wr_32(xl_virt_base, EXT_MEM_CNTL, 0xE0000C01);
        ati_wr_16(xl_virt_base, CRTC_GEN_CNTL + 2, 0x0000);
        ati_wr_32(xl_virt_base, DAC_CNTL, ati_config.chip.dac_cntl);
        ati_wr_16(xl_virt_base, GEN_TEST_CNTL, 0x0100);
        ati_wr_32(xl_virt_base, CUSTOM_MACRO_CNTL, 0x003C0171);
        ati_wr_32(xl_virt_base, MEM_BUF_CNTL, 0x00382848);

        ati_wr_32(xl_virt_base, HW_DEBUG, ati_config.chip.hw_debug);
        ati_wr_16(xl_virt_base, MEM_ADDR_CONFIG, 0x0000);
        ati_wr_16(xl_virt_base, GP_IO + 2, 0x0000);
        ati_wr_16(xl_virt_base, GEN_TEST_CNTL, 0x0000);
        ati_wr_16(xl_virt_base, EXT_DAC_REGS + 2, 0x0000);
        ati_wr_32(xl_virt_base, CRTC_INT_CNTL, 0x00000000);
        ati_wr_32(xl_virt_base, TIMER_CONFIG, 0x00000000);
        ati_wr_32(xl_virt_base, 0xec, 0x00000000);
        ati_wr_32(xl_virt_base, 0xfc, 0x00000000);

	for (i = 0;; i += 2) {
		if (lcd_tbl[i] < 0)
			break;
		ati_wr_8(xl_virt_base, LCD_INDEX, lcd_tbl[i]);
		ati_wr_32(xl_virt_base, LCD_DATA, lcd_tbl[i + 1]);
	}

	ati_wr_16(xl_virt_base, CONFIG_STAT0, 0x00A4);
        delay(10000);

        ati_wr_8(xl_virt_base, BUS_CNTL + 1, 0xA0);
        delay(10000);

	ati_reset_clocks(&ati_config, 1);
        delay(10000);

        // something about power management
        ati_wr_8(xl_virt_base, LCD_INDEX, 0x08);
        ati_wr_8(xl_virt_base, LCD_DATA, 0x0A);
        ati_wr_8(xl_virt_base, LCD_INDEX, 0x08);
        ati_wr_8(xl_virt_base, LCD_DATA+3, 0x02);
        ati_wr_8(xl_virt_base, LCD_INDEX, 0x08);
        ati_wr_8(xl_virt_base, LCD_DATA, 0x0B);
        delay(2000);

        // enable display requests, enable CRTC
        ati_wr_8(xl_virt_base, CRTC_GEN_CNTL+3, 0x02);
        // disable display
        ati_wr_8(xl_virt_base, CRTC_GEN_CNTL, 0x40);
        // disable display requests, disable CRTC
        ati_wr_8(xl_virt_base, CRTC_GEN_CNTL + 3, 0x04);
        delay(10000);

        ati_wr_pll(xl_virt_base, PLL_YCLK_CNTL, 0x25);

	ati_wr_16(xl_virt_base, CUSTOM_MACRO_CNTL, 0x0179);
        ati_wr_16(xl_virt_base, CUSTOM_MACRO_CNTL+2, 0x005E);
        ati_wr_16(xl_virt_base, CUSTOM_MACRO_CNTL+2,
	    ati_config.chip.macro_cntl>>16);
        ati_wr_8(xl_virt_base, CUSTOM_MACRO_CNTL+1,
	    (ati_config.chip.macro_cntl>>8) & 0xff);

        ati_wr_32(xl_virt_base, MEM_ADDR_CONFIG,
	    ati_config.chip.mem_addr_conf);
        ati_wr_32(xl_virt_base, MEM_CNTL, ati_config.chip.mem_cntl);
        ati_wr_32(xl_virt_base, EXT_MEM_CNTL, ati_config.chip.ext_mem_cntl);

        ati_wr_8(xl_virt_base, CONFIG_STAT0, 0xa0 | ati_config.chip.mem_type);

        ati_wr_pll(xl_virt_base, PLL_YCLK_CNTL, 0x01);
        delay(15000);
        ati_wr_pll(xl_virt_base, PLL_YCLK_CNTL, ati_config.chip.yclk_cntl);
        delay(1000);

	ati_reset_clocks(&ati_config, 0);
        delay(50000);
	ati_reset_clocks(&ati_config, 0);
        delay(50000);

	ati_wr_8(xl_virt_base, BUS_CNTL+3, 0x7B);
        delay(1000);
        ati_wr_8(xl_virt_base, BUS_CNTL+3, 0x73);

        ati_wr_8(xl_virt_base, CONFIG_STAT0, 0x80 | ati_config.chip.mem_type);

        // disable display requests, disable CRTC
        ati_wr_8(xl_virt_base, CRTC_GEN_CNTL+3, 0x04);

        // disable mapping registers in VGA aperture
	ati_wr_8(xl_virt_base, CONFIG_CNTL,
	    ati_rd_8(xl_virt_base, CONFIG_CNTL) & ~0x04);
        delay(50000);

        // enable display requests, enable CRTC
        ati_wr_8(xl_virt_base, CRTC_GEN_CNTL+3, 0x02);

        // make GPIO's 14,15,16 all inputs
        ati_wr_8(xl_virt_base, LCD_INDEX, 0x07);
        ati_wr_8(xl_virt_base, LCD_DATA+3, 0x00);

        // enable the display
        ati_wr_8(xl_virt_base, CRTC_GEN_CNTL, 0x00);
        delay(17000);
        // reset the memory controller
        ati_wr_8(xl_virt_base, GEN_TEST_CNTL+1, 0x02);
        delay(15000);
        ati_wr_8(xl_virt_base, GEN_TEST_CNTL+1, 0x00);
        delay(30000);

        // enable extended register block
        ati_wr_8(xl_virt_base, BUS_CNTL+3, ati_rd_8(xl_virt_base, BUS_CNTL+3) | 0x08);
        // set FIFO size to 512 (PIO)
        ati_wr_32(xl_virt_base, GUI_CNTL,
	    ati_rd_32(xl_virt_base, GUI_CNTL) & ~0x3);

        // enable CRT and disable lcd
        ati_wr_8(xl_virt_base, LCD_INDEX, 0x01);
        pdata = ati_rd_32(xl_virt_base, LCD_DATA);
        pdata = (pdata | 0x01) & ~0x02;
        ati_wr_32(xl_virt_base, LCD_DATA, pdata);

	/* Set default screen parameters */
	ati_set_par(&ati_config);
#endif

	/* Set default color map */
	ati_set_palette(&ati_config);

	/* Zap the video memory */
	memset((void *)ati_config.map.ati_vid_vbase, 0,
	    ati_config.map.ati_vid_size);

	/* Set font to use */
	ati_config.map.font = &vt220l8x10;

	ati_config_done++;

#ifdef ATI_DEBUG
	ati_dump_pll(&ati_config);
#endif

#ifdef LOGOSPLASH
	ati_splash(&ati_config);
#endif

        return;
}

static int
ati_valid_pll(struct ati_config *config)
{
static char clk_div[] = { -1, 8, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1 };
	int q;
	int pixclockperiod;
	struct ati_pll *pll;

	pll = &config->pll;
	pll->ref_div = pll->pll_period * 2 * 255 / pll->refclk_period;
	q = pll->refclk_period * pll->ref_div * 4 / pll->mclk_period;
	if (q <= 128 || q > 2040)
		return -1;

	pll->mclk_post_div_real = clk_div[q / 128];
	pll->mclk_fb_div = q * pll->mclk_post_div_real / 8;

	pixclockperiod = 1000000000 / config->vpar.pixclock;
	q = pll->refclk_period * pll->ref_div * 4 / pixclockperiod;
	if (q <= 128 || q > 2040)
		return -1;
	pll->vclk_post_div_real = clk_div[q / 128];
	pll->vclk_fb_div = q * pll->vclk_post_div_real / 8;

	return 0;
}

static void
ati_calc_pll(struct ati_config *config)
{
	struct ati_pll *pll;
	int postdiv;

	pll = &config->pll;
	pll->gen_cntl = 0x04;	/* OSC Enable */

	switch(pll->mclk_post_div_real) {
	case 1:
		postdiv = 0;
		break;
	case 2:
		postdiv = 1;
		break;
	case 3:
		postdiv = 4;
		break;
	case 4:
		postdiv = 2;
		break;
	case 8:
		postdiv = 3;
		break;
	}
	pll->gen_cntl |= postdiv << 4;
	pll->ext_cntl = postdiv;

	switch(pll->vclk_post_div_real) {
	case 1:
		pll->vclk_post_div = 0;
		break;
	case 2:
		pll->vclk_post_div = 1;
		break;
	case 3:
		pll->ext_cntl |= 0x10;
		pll->vclk_post_div = 0;
		break;
	case 4:
		pll->vclk_post_div = 2;
		break;
	case 6:
		pll->ext_cntl |= 0x10;
		pll->vclk_post_div = 2;
		break;
	case 8:
		pll->vclk_post_div = 3;
		break;
	case 12:
		pll->ext_cntl |= 0x10;
		pll->vclk_post_div = 3;
		break;
	}
	pll->vclk_cntl = 0x03;
}

static int
ati_dsp_gt(struct ati_config *config)
{
	int xclks_per_row, dsp_loop_latency, dsp_precision, dsp_off, dsp_on;
	int dsp_xclks_per_row, fifo_off, fifo_on, y;
	int n, fifo;
	struct ati_pll *pll;

	pll = &config->pll;
	fifo = 32;
	xclks_per_row = pll->mclk_fb_div * pll->vclk_post_div_real *64 << 11;
	xclks_per_row /= config->vpar.bits_per_pixel * pll->vclk_fb_div *
	    pll->mclk_post_div_real;

	y = xclks_per_row * fifo >> 11;
	dsp_precision = 0;
	while(y >>= 1)
		dsp_precision++;
	dsp_precision -= 5;

	fifo_off = (xclks_per_row * (fifo -1)) / fifo; + (3 << 6);

	if (config->chip.mem_size > 1*MB) {
		n = 8;
		dsp_loop_latency = 10;
	} else {
		return -1;
	}

	if (dsp_xclks_per_row >= (n << 11))
		fifo_on = ((2 * n + 1) << 6) + (xclks_per_row >> 5);
	else
		fifo_on = (3 * n + 2) << 6;

	dsp_xclks_per_row = xclks_per_row >> dsp_precision;
	dsp_on = fifo_on >> dsp_precision;
	dsp_off = fifo_off >> dsp_precision;

	config->pll.dsp_config = (dsp_xclks_per_row & 0x3fff) |
	    ((dsp_loop_latency & 0xf) << 16) | ((dsp_precision & 0x7) << 20);
	config->pll.dsp_on_off = (dsp_off & 0x7ff) | ((dsp_on & 0x7ff) << 16);	
	return 0;
}


int
ati_var_to_pll(struct ati_config *config)
{
	int error;

	config->pll.pll_period = 1000000000 / config->chip.pll;
	config->pll.mclk_period = 1000000000 / config->chip.mclk;
	config->pll.refclk_period = 1000000000 / config->chip.refclk;

	error = ati_valid_pll(config);
	if (error)
		return error;
	if (config->chip.magic & GTB_DSP)
		error = ati_dsp_gt(config);
		if (error)
			return error;
	ati_calc_pll(config);
	return 0;
}

void
ati_set_pll(struct ati_config *config)
{
	vaddr_t ati_base = ati_config.map.ati_ctrl_vbase;

	ati_wr_pll(ati_base, PLL_REF_DIV, config->pll.ref_div);
	ati_wr_pll(ati_base, PLL_GEN_CNTL, config->pll.gen_cntl);
	ati_wr_pll(ati_base, MCLK_FB_DIV, config->pll.mclk_fb_div);
	ati_wr_pll(ati_base, PLL_VCLK_CNTL, config->pll.vclk_cntl);
	ati_wr_pll(ati_base, VCLK_POST_DIV, config->pll.vclk_post_div);
	ati_wr_pll(ati_base, VCLK0_FB_DIV, config->pll.vclk_fb_div);
	ati_wr_pll(ati_base, PLL_EXT_CNTL, config->pll.ext_cntl);

	if (config->chip.magic & GTB_DSP) {
		if (config->chip.magic & XL_DLL)
			ati_wr_pll(ati_base, DLL_CNTL, 0x80);
		else if (config->chip.mem_type >= SDRAM)
			ati_wr_pll(ati_base, DLL_CNTL, 0xa6);
		else
			ati_wr_pll(ati_base, DLL_CNTL, 0xa0);
		ati_wr_pll(ati_base, VFC_CNTL, 0x1b);
		ati_wr_32(ati_base, DSP_CONFIG, config->pll.dsp_config);
		ati_wr_32(ati_base, DSP_ON_OFF, config->pll.dsp_on_off);
	}
}

#ifdef ATI_DEBUG
void
ati_dump_pll(struct ati_config *config)
{
	struct ati_pll *pll;

	pll = &config->pll;
	printf("ATI PLL parameters:\n");
	printf("refclk_period      %p\n", pll->refclk_period);
	printf("pll_period         %p\n", pll->pll_period);
	printf("mclk_period        %p\n", pll->mclk_period);
	printf("ref_div            %p\n", pll->ref_div);
	printf("ext_cntl           %p\n", pll->ext_cntl);
	printf("vclk_cntl          %p\n", pll->vclk_cntl);
	printf("mclk_post_div_real %p\n", pll->mclk_post_div_real);
	printf("mclk_fb_div        %p\n", pll->mclk_fb_div);
	printf("vclk_post_div_real %p\n", pll->vclk_post_div_real);
	printf("vclk_post_div      %p\n", pll->vclk_post_div);
	printf("vclk_fb_div        %p\n", pll->vclk_fb_div);
	printf("gen_cntl           %p\n", pll->gen_cntl);
	printf("dsp_on_off         %p\n", pll->dsp_on_off);
	printf("dsp_config         %p\n", pll->dsp_config);
}
#endif

/*
 *  Render a character on the screen.
 */
void
ati_render_char(u_char c, int xpos, int ypos, int fg, int bg)
{
	struct ati_config *config = &ati_config;
	struct wsdisplay_font *font = ati_config.map.font;
	u_char *fontchar;
	u_char *renderstart, *renderpos;
	int fdata;
	int x, y, pix;

	if (!ati_config_done)
		return;

	/* Check if char is in table */
	if (c < font->firstchar || c >= (font->firstchar + font->numchars))
		return;

	/* Find character in font data */
	fontchar = font->data;
	fontchar += (c - font->firstchar) * font->fontheight * font->stride;

	renderstart = (u_char *)config->map.ati_vid_vbase;
	renderstart += ypos * font->fontheight * config->vpar.xres;
	renderstart += xpos * font->fontwidth;

	for (y = font->fontheight; y > 0; y--) {
		renderpos = renderstart;
		pix = (1 << font->fontwidth - 1);
		fdata = *fontchar;
		for (x = font->fontwidth; x > 0; x--, pix >>= 1) {
			renderpos = (void *)((long)renderpos ^ 1);
			if (fdata & pix)
				*renderpos = fg;
			else
				*renderpos = bg;
			renderpos = (void *)((long)renderpos ^ 1);
			renderpos++;
		}
		renderstart += config->vpar.xres;
		fontchar += font->stride;
	}
}

void
ati_scroll(int ydst, int ysrc, int size)
{
	struct ati_config *config = &ati_config;
	struct wsdisplay_font *font = ati_config.map.font;
	u_char *scrolldst, *scrollsrc;
	size_t scrollsiz, step;

	scrolldst = (void *)config->map.ati_vid_vbase;
	scrollsrc = scrolldst;

	scrolldst += ydst * font->fontheight * config->vpar.xres;
	scrollsrc += ysrc * font->fontheight * config->vpar.xres;
	scrollsiz = size * font->fontheight * config->vpar.xres;

	bcopy(scrollsrc, scrolldst, scrollsiz);
}

void
ati_cursor(int xpos, int ypos, int fg, int bg)
{
	struct ati_config *config = &ati_config;
	struct wsdisplay_font *font = ati_config.map.font;
	u_char *renderstart, *renderpos;
	int x, y;

	if (!ati_config_done)
		return;

	renderstart = (char *)config->map.ati_vid_vbase;
	renderstart += ypos * font->fontheight * config->vpar.xres;
	renderstart += xpos * font->fontwidth;

	for (y = font->fontheight; y > 0; y--) {
		renderpos = renderstart;
		for (x = font->fontwidth; x > 0; x--) {
			renderpos = (void *)((long)renderpos ^ 1);
			if (*renderpos == fg)
				*renderpos = bg;
			else
				*renderpos = fg;
			renderpos = (void *)((long)renderpos ^ 1);
			renderpos++;
		}
		renderstart += config->vpar.xres;
	}
}

void
ati_dsp_enable(int on)
{
	struct ati_config *config = &ati_config;
	vaddr_t ati_base = ati_config.map.ati_ctrl_vbase;

	switch (on) {
	case 0:
		ati_wr_32(ati_base, CRTC_GEN_CNTL, 0);
		break;
	case 1:
		ati_wr_32(ati_base, CRTC_GEN_CNTL, config->scan.gen_cntl);
		ati_wr_32(ati_base, CRTC_OFF_PITCH, config->scan.off_pitch);
		break;
	case 2:
		ati_splash_saver(config);
		break;
	}
}

#ifdef ATI_DEBUG
static void
aty_xl_powerup(struct ati_config *config)
{

	vaddr_t ati_base = config->map.ati_ctrl_vbase;
/*----- INIT_PLL -----*/
        ati_wr_8(ati_base, CONFIG_STAT0, 0x85);
        ati_wr_pll(ati_base, LVDS_CNTL0, 0x0);    /*portio pll*/
        ati_wr_pll(ati_base, DLL2_CNTL, 0x10);   /*portio pll*/
        ati_wr_pll(ati_base, V2PLL_CNTL, 0x10);   /*portio pll*/
        ati_wr_pll(ati_base, MPLL_CNTL, 0xad);    /*portio pll*/
        ati_wr_pll(ati_base, VPLL_CNTL, 0xd5);    /*portio pll*/
        ati_wr_pll(ati_base, PLL_VCLK_CNTL, 0x0);     /*portio pll*/
        ati_wr_pll(ati_base, VFC_CNTL, 0x1b);    /*portio pll*/
        ati_wr_pll(ati_base, PLL_REF_DIV, 0x1f);    /*portio pll*/
        ati_wr_pll(ati_base, PLL_EXT_CNTL, 0xa);     /*portio pll*/
        ati_wr_pll(ati_base, SPLL_CNTL2, 0x3);    /*portio pll*/
        ati_wr_pll(ati_base, PLL_GEN_CNTL, 0x54);    /*portio pll*/
        delay(1000);
        ati_wr_8(ati_base, 0xd1, 0x1);
        ati_wr_8(ati_base, 0xd1, 0x0);
        ati_wr_8(ati_base, 0xd1, 0x2);
        delay(5000);
        ati_wr_pll(ati_base, 0x4, 0x88);    /*portio pll*/
        delay(15000);
        ati_wr_pll(ati_base, 0x3, 0x54);    /*portio pll*/
        ati_wr_pll(ati_base, 0xc, 0x82);    /*portio pll*/
        ati_wr_pll(ati_base, 0xc, 0xe2);    /*portio pll*/
        delay(5000);
        ati_wr_pll(ati_base, 0xc, 0x82);    /*portio pll*/
        delay(5000);
        delay(1000);
        ati_wr_8(ati_base, 0xd1, 0x0);
        delay(5000);
        ati_wr_8(ati_base, 0x1f, 0x4);
        delay(5000);
        delay(1000);
        ati_wr_8(ati_base, 0xac, 0x83);
        ati_wr_8(ati_base, 0xac, 0x8b);
        ati_wr_8(ati_base, 0xac, 0x8f);
        delay(5000);
        ati_wr_8(ati_base, 0xac, 0x81);
        delay(5000);
        ati_wr_8(ati_base, 0x1f, 0x4);
        ati_wr_pll(ati_base, 0x3, 0x44);    /*portio pll*/
        ati_wr_pll(ati_base, 0x5, 0x3);     /*portio pll*/
/* - <<<<<<<<<<<<End of table>>>>>>>>>>>>>- */
/*----- INIT_REGISTER -----*/
        ati_wr_8(ati_base, 0xa0, 0x1);
        /*pci_write_config_byte(ati_base, pdev, 0x4, 0x87);*/
        ati_wr_16(ati_base, 0xa2, 0x7b33);
        ati_wr_16(ati_base, 0xa0, 0xa001);
        ati_wr_16(ati_base, 0x1c, 0x200);
        ati_wr_16(ati_base, 0x1e, 0x400);
        ati_wr_16(ati_base, CONFIG_STAT0, 0x20);
        ati_wr_16(ati_base, 0xb0, 0x1a33);
        ati_wr_16(ati_base, 0xb2, 0x1015);
        ati_wr_16(ati_base, 0xac, 0x1301);
        ati_wr_16(ati_base, 0x0, 0x10c);
        ati_wr_16(ati_base, 0x0, 0x0);
        ati_wr_16(ati_base, 0x0, 0xa);
        ati_wr_16(ati_base, 0xc6, 0x8000);
        ati_wr_16(ati_base, 0xd0, 0x100);
        ati_wr_16(ati_base, 0xd4, 0x171);
        ati_wr_16(ati_base, 0xd6, 0x3c);
        ati_wr_16(ati_base, 0x2c, 0x2848);
        ati_wr_16(ati_base, 0x2e, 0x38);
        ati_wr_16(ati_base, 0x4c, 0xa03);
        ati_wr_16(ati_base, 0x4e, 0x13);
        ati_wr_16(ati_base, 0xc, 0x4e);
        ati_wr_16(ati_base, 0x0, 0x0);
        ati_wr_16(ati_base, 0x7c, 0x3800);
        ati_wr_16(ati_base, 0x7e, 0x4883);
        ati_wr_16(ati_base, 0x34, 0x0);
        ati_wr_16(ati_base, 0x7a, 0x0);
        ati_wr_16(ati_base, 0xd0, 0x0);
        ati_wr_16(ati_base, 0xca, 0x0);
        ati_wr_32(ati_base, 0x18, 0x0);
        ati_wr_32(ati_base, 0x80, 0x0);
        ati_wr_32(ati_base, 0x84, 0x0);
        ati_wr_32(ati_base, 0x88, 0x0);
        ati_wr_32(ati_base, 0x8c, 0x0);
        ati_wr_32(ati_base, 0x28, 0x0);
        ati_wr_32(ati_base, 0xec, 0x0);
        ati_wr_32(ati_base, 0xfc, 0x0);
        ati_wr_32(ati_base, 0xa4, 0x1); /*index*/
        delay(5000);
        ati_wr_32(ati_base, 0xa8, 0x520c0);
        ati_wr_32(ati_base, 0xa4, 0x8); /*index*/
        delay(5000);
        ati_wr_32(ati_base, 0xa8, 0x2000408);
        ati_wr_32(ati_base, 0xa4, 0x3); /*index*/
        delay(5000);
        ati_wr_32(ati_base, 0xa8, 0xf00);
        ati_wr_32(ati_base, 0xa4, 0x0); /*index*/
        delay(5000);
        ati_wr_32(ati_base, 0xa8, 0x0);
        ati_wr_32(ati_base, 0xa4, 0x2); /*index*/
        delay(5000);
        ati_wr_32(ati_base, 0xa8, 0x0);
        ati_wr_32(ati_base, 0xa4, 0x4); /*index*/
        delay(5000);
        ati_wr_32(ati_base, 0xa8, 0x0);
        ati_wr_32(ati_base, 0xa4, 0x5); /*index*/
        delay(5000);
        ati_wr_32(ati_base, 0xa8, 0x0);
        ati_wr_32(ati_base, 0xa4, 0x6); /*index*/
        delay(5000);
        ati_wr_32(ati_base, 0xa8, 0x0);
        ati_wr_32(ati_base, 0xa4, 0x33); /*index*/
        delay(5000);
        ati_wr_32(ati_base, 0xa8, 0x0);
        ati_wr_32(ati_base, 0xa4, 0x34); /*index*/
        delay(5000);
        ati_wr_32(ati_base, 0xa8, 0x0);
        ati_wr_32(ati_base, 0xa4, 0x35); /*index*/
        delay(5000);
        ati_wr_32(ati_base, 0xa8, 0x0);
        ati_wr_32(ati_base, 0xa4, 0x36); /*index*/
        delay(5000);
        ati_wr_32(ati_base, 0xa8, 0x0);
        ati_wr_32(ati_base, 0xa4, 0x37); /*index*/
        delay(5000);
        ati_wr_32(ati_base, 0xa8, 0x0);
        ati_wr_32(ati_base, 0xa4, 0x7); /*index*/
        delay(5000);
        ati_wr_16(ati_base, 0xaa, 0x0);
        ati_wr_32(ati_base, 0xa4, 0x9); /*index*/
        delay(5000);
        ati_wr_16(ati_base, 0xaa, 0x0);
        ati_wr_16(ati_base, CONFIG_STAT0, 0xa4);
        ati_wr_8(ati_base, 0xa1, 0xa0);
        delay(1000);
        ati_wr_8(ati_base, 0xd1, 0x1);
        ati_wr_8(ati_base, 0xd1, 0x0);
        ati_wr_8(ati_base, 0xd1, 0x2);
        delay(5000);
        ati_wr_pll(ati_base, 0x4, 0x88);    /*portio pll*/
        delay(15000);
        ati_wr_pll(ati_base, 0x3, 0x44);    /*portio pll*/
        ati_wr_pll(ati_base, 0xc, 0x82);    /*portio pll*/
        ati_wr_pll(ati_base, 0xc, 0xe2);    /*portio pll*/
        delay(5000);
        ati_wr_pll(ati_base, 0xc, 0x82);    /*portio pll*/
        delay(5000);
        delay(1000);
        ati_wr_8(ati_base, 0xd1, 0x0);
        delay(5000);
        ati_wr_8(ati_base, 0x1f, 0x4);
        delay(5000);
        delay(1000);
        ati_wr_8(ati_base, 0xac, 0x3);
        ati_wr_8(ati_base, 0xac, 0xb);
        ati_wr_8(ati_base, 0xac, 0xf);
        delay(5000);
        ati_wr_8(ati_base, 0xac, 0x1);
        delay(5000);
        ati_wr_8(ati_base, 0x1f, 0x4);
        ati_wr_pll(ati_base, 0x3, 0x44);    /*portio pll*/
        ati_wr_pll(ati_base, 0x5, 0x3);     /*portio pll*/
        ati_wr_32(ati_base, 0xa4, 0x8); /*index*/
        delay(5000);
        ati_wr_8(ati_base, 0xa8, 0xa);
        ati_wr_32(ati_base, 0xa4, 0x8); /*index*/
        delay(5000);
        ati_wr_8(ati_base, 0xab, 0x2);
        ati_wr_32(ati_base, 0xa4, 0x8); /*index*/
        delay(5000);
        ati_wr_8(ati_base, 0xa8, 0xb);
/* - <<<<<<<<<<<<End of table>>>>>>>>>>>>>- */
/*----- INIT_MEMORY -----*/
        ati_wr_8(ati_base, 0x1f, 0x4);
        delay(5000);
        ati_wr_pll(ati_base, 0x29, 0x25);   /*portio pll*/
        ati_wr_16(ati_base, 0xd4, 0x179);
        ati_wr_16(ati_base, 0xd6, 0x5e);
        ati_wr_16(ati_base, 0xd6, 0x7f);
        ati_wr_8(ati_base, 0xd5, 0x7);
        ati_wr_32(ati_base, 0x34, 0x200213);
        ati_wr_32(ati_base, 0xb0, 0x165a2b);
        ati_wr_32(ati_base, 0xac, 0xe0000cf1);
        ati_wr_8(ati_base, CONFIG_STAT0, 0xa6);
        ati_wr_pll(ati_base, 0x29, 0x1);    /*portio pll*/
        delay(15000);
        ati_wr_pll(ati_base, 0x29, 0x19);   /*portio pll*/
        delay(1000);
        ati_wr_8(ati_base, 0xd1, 0x1);
        ati_wr_8(ati_base, 0xd1, 0x0);
        ati_wr_8(ati_base, 0xd1, 0x2);
        delay(5000);
        ati_wr_pll(ati_base, 0x4, 0x88);    /*portio pll*/
        delay(15000);
        ati_wr_pll(ati_base, 0x3, 0x44);    /*portio pll*/
        ati_wr_pll(ati_base, 0xc, 0x82);    /*portio pll*/
        ati_wr_pll(ati_base, 0xc, 0xe2);    /*portio pll*/
        delay(5000);
        ati_wr_pll(ati_base, 0xc, 0x82);    /*portio pll*/
        delay(5000);
        delay(1000);
        ati_wr_8(ati_base, 0xd1, 0x0);
        delay(5000);
        ati_wr_8(ati_base, 0x1f, 0x4);
        delay(5000);
        delay(1000);
        ati_wr_8(ati_base, 0xac, 0xf3);
        ati_wr_8(ati_base, 0xac, 0xfb);
        ati_wr_8(ati_base, 0xac, 0xff);
        delay(5000);
        ati_wr_8(ati_base, 0xac, 0xf1);
        delay(5000);
        ati_wr_8(ati_base, 0x1f, 0x4);
        ati_wr_pll(ati_base, 0x3, 0x44);    /*portio pll*/
        ati_wr_pll(ati_base, 0x5, 0x3);     /*portio pll*/
        delay(50000);
        delay(1000);
        ati_wr_8(ati_base, 0xd1, 0x1);
        ati_wr_8(ati_base, 0xd1, 0x0);
        ati_wr_8(ati_base, 0xd1, 0x2);
        delay(5000);
        ati_wr_pll(ati_base, 0x4, 0x88);    /*portio pll*/
        delay(15000);
        ati_wr_pll(ati_base, 0x3, 0x44);    /*portio pll*/
        ati_wr_pll(ati_base, 0xc, 0x82);    /*portio pll*/
        ati_wr_pll(ati_base, 0xc, 0xe2);    /*portio pll*/
        delay(5000);
        ati_wr_pll(ati_base, 0xc, 0x82);    /*portio pll*/
        delay(5000);
        delay(1000);
        ati_wr_8(ati_base, 0xd1, 0x0);
        delay(5000);
        ati_wr_8(ati_base, 0x1f, 0x4);
        delay(5000);
        delay(1000);
        ati_wr_8(ati_base, 0xac, 0xf3);
        ati_wr_8(ati_base, 0xac, 0xfb);
        ati_wr_8(ati_base, 0xac, 0xff);
        delay(5000);
        ati_wr_8(ati_base, 0xac, 0xf1);
        delay(5000);
        ati_wr_8(ati_base, 0x1f, 0x4);
        ati_wr_pll(ati_base, 0x3, 0x44);    /*portio pll*/
        ati_wr_pll(ati_base, 0x5, 0x3);     /*portio pll*/
        ati_wr_16(ati_base, 0x456, 0x0);
        ati_wr_32(ati_base, 0x5b8, 0x0);
        ati_wr_32(ati_base, 0x1fc, 0xc0);
        ati_wr_32(ati_base, 0x704, 0x0);
        ati_wr_32(ati_base, 0x1fc, 0x0);
        ati_wr_16(ati_base, 0x1b4, 0x44e0);
        ati_wr_8(ati_base, CONFIG_STAT0, 0x86);
        ati_wr_8(ati_base, 0x1f, 0x4);
/* - <<<<<<<<<<<<End of table>>>>>>>>>>>>>- */
        delay(15000);

    delay(5000);

    /*SetVCLK*/
    ati_wr_pll(ati_base, VCLK_POST_DIV, 0xff);
    ati_wr_pll(ati_base, VCLK0_FB_DIV, 0xda);
    ati_wr_pll(ati_base, VCLK1_FB_DIV, 0xf5);
    ati_wr_pll(ati_base, VCLK2_FB_DIV, 0x00);
    ati_wr_pll(ati_base, VCLK3_FB_DIV, 0xda);
    ati_wr_pll(ati_base, PLL_EXT_CNTL, 0x4a);

    ati_wr_32(ati_base, CRTC_GEN_CNTL, 0x020b2400);
    ati_wr_32(ati_base, CRTC_H_TOTAL_DISP, 0x004f0063);
    ati_wr_32(ati_base, CRTC_H_SYNC_STRT_WID, 0x002c0152);
    ati_wr_32(ati_base, CRTC_V_TOTAL_DISP, 0x01df020c);
    ati_wr_32(ati_base, CRTC_V_SYNC_STRT_WID, 0x002201e9);
/*
    ati_wr_32(ati_base, CRTC_CRNT_VLINE, 0x016003ff);
*/
    ati_wr_32(ati_base, CRTC_OFF_PITCH, 0x14000000);
    ati_wr_32(ati_base, CRTC_INT_CNTL, 0x80000114);
    ati_wr_32(ati_base, CLOCK_CNTL, 0x00da2803);
    ati_wr_32(ati_base, DSP_CONFIG, 0x004804fe);
    ati_wr_32(ati_base, DSP_ON_OFF, 0x008404D0);

//    ati_wr_32(ati_base, SCRATCH_REG0, 0x00800000);
//    ati_wr_32(ati_base, SCRATCH_REG1, 0x00401000);
    ati_wr_32(ati_base, DAC_CNTL, 0x8401010A);
    ati_wr_32(ati_base, BUS_CNTL, 0x7323a001);

    ati_wr_32(ati_base, CRTC_GEN_CNTL, 0x030b2400);
    ati_wr_32(ati_base, LCD_INDEX, 0x01);
    ati_wr_32(ati_base, LCD_DATA, 0x000520c1);

}
#endif
