/*
   radeon_vid - VIDIX based video driver for Radeon and Rage128 chips
   Copyrights 2002 Nick Kurshev. This file is based on sources from
   GATOS (gatos.sf.net) and X11 (www.xfree86.org)
   Licence: GPL

   31.12.2002 added support for fglrx drivers by Marcel Naziri (zwobbl@zwobbl.de)
   6.04.2004 fixes to allow compiling vidix without X11 (broken in original patch)
   PPC support by Alex Beregszaszi
*/

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <inttypes.h>

#include "../../config.h"
#include "../../bswap.h"
#include "../../libdha/pci_ids.h"
#include "../../libdha/pci_names.h"
#include "../vidix.h"
#include "../fourcc.h"
#include "../../libdha/libdha.h"
#include "radeon.h"

#ifdef HAVE_X11
#include <X11/Xlib.h>
#endif

#ifdef RAGE128
#define RADEON_MSG "[rage128]"
#define X_ADJUST 0
#else
#define RADEON_MSG "[radeon]"
#define X_ADJUST (is_shift_required ? 8 : 0)
#ifndef RADEON
#define RADEON
#endif
#endif

static int __verbose = 0;
#ifdef RADEON
static int is_shift_required = 0;
#endif

typedef struct bes_registers_s
{
  /* base address of yuv framebuffer */
  uint32_t yuv_base;
  uint32_t fourcc;
  uint32_t dest_bpp;
  /* YUV BES registers */
  uint32_t reg_load_cntl;
  uint32_t h_inc;
  uint32_t step_by;
  uint32_t y_x_start;
  uint32_t y_x_end;
  uint32_t v_inc;
  uint32_t p1_blank_lines_at_top;
  uint32_t p23_blank_lines_at_top;
  uint32_t vid_buf_pitch0_value;
  uint32_t vid_buf_pitch1_value;
  uint32_t p1_x_start_end;
  uint32_t p2_x_start_end;
  uint32_t p3_x_start_end;
  uint32_t base_addr;
  uint32_t vid_buf_base_adrs_y[VID_PLAY_MAXFRAMES];
  uint32_t vid_buf_base_adrs_u[VID_PLAY_MAXFRAMES];
  uint32_t vid_buf_base_adrs_v[VID_PLAY_MAXFRAMES];
  uint32_t vid_nbufs;

  uint32_t p1_v_accum_init;
  uint32_t p1_h_accum_init;
  uint32_t p23_v_accum_init;
  uint32_t p23_h_accum_init;
  uint32_t scale_cntl;
  uint32_t exclusive_horz;
  uint32_t auto_flip_cntl;
  uint32_t filter_cntl;
  uint32_t key_cntl;
  uint32_t test;
  /* Configurable stuff */
  int double_buff;
  
  int brightness;
  int saturation;
  
  int ckey_on;
  uint32_t graphics_key_clr;
  uint32_t graphics_key_msk;
  uint32_t ckey_cntl;
  
  int deinterlace_on;
  uint32_t deinterlace_pattern;
  
} bes_registers_t;

typedef struct video_registers_s
{
  const char * sname;
  uint32_t name;
  uint32_t value;
}video_registers_t;

static bes_registers_t besr;
#ifndef RAGE128
static int RadeonFamily=100;
#endif
#define DECLARE_VREG(name) { #name, name, 0 }
static video_registers_t vregs[] = 
{
  DECLARE_VREG(VIDEOMUX_CNTL),
  DECLARE_VREG(VIPPAD_MASK),
  DECLARE_VREG(VIPPAD1_A),
  DECLARE_VREG(VIPPAD1_EN),
  DECLARE_VREG(VIPPAD1_Y),
  DECLARE_VREG(OV0_Y_X_START),
  DECLARE_VREG(OV0_Y_X_END),
  DECLARE_VREG(OV0_PIPELINE_CNTL),
  DECLARE_VREG(OV0_EXCLUSIVE_HORZ),
  DECLARE_VREG(OV0_EXCLUSIVE_VERT),
  DECLARE_VREG(OV0_REG_LOAD_CNTL),
  DECLARE_VREG(OV0_SCALE_CNTL),
  DECLARE_VREG(OV0_V_INC),
  DECLARE_VREG(OV0_P1_V_ACCUM_INIT),
  DECLARE_VREG(OV0_P23_V_ACCUM_INIT),
  DECLARE_VREG(OV0_P1_BLANK_LINES_AT_TOP),
  DECLARE_VREG(OV0_P23_BLANK_LINES_AT_TOP),
#ifdef RADEON
  DECLARE_VREG(OV0_BASE_ADDR),
#endif
  DECLARE_VREG(OV0_VID_BUF0_BASE_ADRS),
  DECLARE_VREG(OV0_VID_BUF1_BASE_ADRS),
  DECLARE_VREG(OV0_VID_BUF2_BASE_ADRS),
  DECLARE_VREG(OV0_VID_BUF3_BASE_ADRS),
  DECLARE_VREG(OV0_VID_BUF4_BASE_ADRS),
  DECLARE_VREG(OV0_VID_BUF5_BASE_ADRS),
  DECLARE_VREG(OV0_VID_BUF_PITCH0_VALUE),
  DECLARE_VREG(OV0_VID_BUF_PITCH1_VALUE),
  DECLARE_VREG(OV0_AUTO_FLIP_CNTL),
  DECLARE_VREG(OV0_DEINTERLACE_PATTERN),
  DECLARE_VREG(OV0_SUBMIT_HISTORY),
  DECLARE_VREG(OV0_H_INC),
  DECLARE_VREG(OV0_STEP_BY),
  DECLARE_VREG(OV0_P1_H_ACCUM_INIT),
  DECLARE_VREG(OV0_P23_H_ACCUM_INIT),
  DECLARE_VREG(OV0_P1_X_START_END),
  DECLARE_VREG(OV0_P2_X_START_END),
  DECLARE_VREG(OV0_P3_X_START_END),
  DECLARE_VREG(OV0_FILTER_CNTL),
  DECLARE_VREG(OV0_FOUR_TAP_COEF_0),
  DECLARE_VREG(OV0_FOUR_TAP_COEF_1),
  DECLARE_VREG(OV0_FOUR_TAP_COEF_2),
  DECLARE_VREG(OV0_FOUR_TAP_COEF_3),
  DECLARE_VREG(OV0_FOUR_TAP_COEF_4),
  DECLARE_VREG(OV0_FLAG_CNTL),
#ifdef RAGE128
  DECLARE_VREG(OV0_COLOUR_CNTL),
#else
  DECLARE_VREG(OV0_SLICE_CNTL),
#endif
  DECLARE_VREG(OV0_VID_KEY_CLR),
  DECLARE_VREG(OV0_VID_KEY_MSK),
  DECLARE_VREG(OV0_GRAPHICS_KEY_CLR),
  DECLARE_VREG(OV0_GRAPHICS_KEY_MSK),
  DECLARE_VREG(OV0_KEY_CNTL),
  DECLARE_VREG(OV0_TEST),
  DECLARE_VREG(OV0_LIN_TRANS_A),
  DECLARE_VREG(OV0_LIN_TRANS_B),
  DECLARE_VREG(OV0_LIN_TRANS_C),
  DECLARE_VREG(OV0_LIN_TRANS_D),
  DECLARE_VREG(OV0_LIN_TRANS_E),
  DECLARE_VREG(OV0_LIN_TRANS_F),
  DECLARE_VREG(OV0_GAMMA_0_F),
  DECLARE_VREG(OV0_GAMMA_10_1F),
  DECLARE_VREG(OV0_GAMMA_20_3F),
  DECLARE_VREG(OV0_GAMMA_40_7F),
  DECLARE_VREG(OV0_GAMMA_380_3BF),
  DECLARE_VREG(OV0_GAMMA_3C0_3FF),
  DECLARE_VREG(SUBPIC_CNTL),
  DECLARE_VREG(SUBPIC_DEFCOLCON),
  DECLARE_VREG(SUBPIC_Y_X_START),
  DECLARE_VREG(SUBPIC_Y_X_END),
  DECLARE_VREG(SUBPIC_V_INC),
  DECLARE_VREG(SUBPIC_H_INC),
  DECLARE_VREG(SUBPIC_BUF0_OFFSET),
  DECLARE_VREG(SUBPIC_BUF1_OFFSET),
  DECLARE_VREG(SUBPIC_LC0_OFFSET),
  DECLARE_VREG(SUBPIC_LC1_OFFSET),
  DECLARE_VREG(SUBPIC_PITCH),
  DECLARE_VREG(SUBPIC_BTN_HLI_COLCON),
  DECLARE_VREG(SUBPIC_BTN_HLI_Y_X_START),
  DECLARE_VREG(SUBPIC_BTN_HLI_Y_X_END),
  DECLARE_VREG(SUBPIC_PALETTE_INDEX),
  DECLARE_VREG(SUBPIC_PALETTE_DATA),
  DECLARE_VREG(SUBPIC_H_ACCUM_INIT),
  DECLARE_VREG(SUBPIC_V_ACCUM_INIT),
  DECLARE_VREG(IDCT_RUNS),
  DECLARE_VREG(IDCT_LEVELS),
  DECLARE_VREG(IDCT_AUTH_CONTROL),
  DECLARE_VREG(IDCT_AUTH),
  DECLARE_VREG(IDCT_CONTROL),
  DECLARE_VREG(CONFIG_CNTL)
};

#ifdef HAVE_X11
static uint32_t firegl_shift = 0;
#endif
static void * radeon_mmio_base = 0;
static void * radeon_mem_base = 0; 
static int32_t radeon_overlay_off = 0;
static uint32_t radeon_ram_size = 0;
/* Restore on exit */
static uint32_t SAVED_OV0_GRAPHICS_KEY_CLR = 0;
static uint32_t SAVED_OV0_GRAPHICS_KEY_MSK = 0;
static uint32_t SAVED_OV0_VID_KEY_CLR = 0;
static uint32_t SAVED_OV0_VID_KEY_MSK = 0;
static uint32_t SAVED_OV0_KEY_CNTL = 0;
#ifdef WORDS_BIGENDIAN
static uint32_t SAVED_CONFIG_CNTL = 0;
#if defined(RAGE128)
#define APER_0_BIG_ENDIAN_16BPP_SWAP (1<<0)
#define APER_0_BIG_ENDIAN_32BPP_SWAP (2<<0)
#else
#define RADEON_SURFACE_CNTL                 0x0b00
#define RADEON_NONSURF_AP0_SWP_16BPP (1 << 20)
#define RADEON_NONSURF_AP0_SWP_32BPP (1 << 21)
#endif
#endif

#define GETREG(TYPE,PTR,OFFZ)		(*((volatile TYPE*)((PTR)+(OFFZ))))
#define SETREG(TYPE,PTR,OFFZ,VAL)	(*((volatile TYPE*)((PTR)+(OFFZ))))=VAL

#define INREG8(addr)		GETREG(uint8_t,(uint8_t*)(radeon_mmio_base),addr)
#define OUTREG8(addr,val)	SETREG(uint8_t,(uint8_t*)(radeon_mmio_base),addr,val)

static inline uint32_t INREG (uint32_t addr) {
	uint32_t tmp = GETREG(uint32_t,(uint8_t*)(radeon_mmio_base),addr);
	return le2me_32(tmp);
}
//#define OUTREG(addr,val)	SETREG(uint32_t,(uint8_t*)(radeon_mmio_base),addr,val)
#define OUTREG(addr,val)	SETREG(uint32_t,(uint8_t*)(radeon_mmio_base),addr,le2me_32(val))
#define OUTREGP(addr,val,mask)  					\
	do {								\
		unsigned int _tmp = INREG(addr);			\
		_tmp &= (mask);						\
		_tmp |= (val);						\
		OUTREG(addr, _tmp);					\
	} while (0)

static __inline__ uint32_t INPLL(uint32_t addr)
{
	OUTREG8(CLOCK_CNTL_INDEX, addr & 0x0000001f);
	return (INREG(CLOCK_CNTL_DATA));
}

#define OUTPLL(addr,val)	OUTREG8(CLOCK_CNTL_INDEX, (addr & 0x0000001f) | 0x00000080); \
				OUTREG(CLOCK_CNTL_DATA, val)
#define OUTPLLP(addr,val,mask)  					\
	do {								\
		unsigned int _tmp = INPLL(addr);			\
		_tmp &= (mask);						\
		_tmp |= (val);						\
		OUTPLL(addr, _tmp);					\
	} while (0)

static uint32_t radeon_vid_get_dbpp( void )
{
  uint32_t dbpp,retval;
  dbpp = (INREG(CRTC_GEN_CNTL)>>8)& 0xF;
  switch(dbpp)
  {
    case DST_8BPP: retval = 8; break;
    case DST_15BPP: retval = 15; break;
    case DST_16BPP: retval = 16; break;
    case DST_24BPP: retval = 24; break;
    default: retval=32; break;
  }
  return retval;
}

static int radeon_is_dbl_scan( void )
{
  return (INREG(CRTC_GEN_CNTL))&CRTC_DBL_SCAN_EN;
}

static int radeon_is_interlace( void )
{
  return (INREG(CRTC_GEN_CNTL))&CRTC_INTERLACE_EN;
}

static uint32_t radeon_get_xres( void )
{
  /* FIXME: currently we extract that from CRTC!!!*/
  uint32_t xres,h_total;
  h_total = INREG(CRTC_H_TOTAL_DISP);
  xres = (h_total >> 16) & 0xffff;
  return (xres + 1)*8;
}

static uint32_t radeon_get_yres( void )
{
  /* FIXME: currently we extract that from CRTC!!!*/
  uint32_t yres,v_total;
  v_total = INREG(CRTC_V_TOTAL_DISP);
  yres = (v_total >> 16) & 0xffff;
  return yres + 1;
}

/* get flat panel x resolution*/
static uint32_t radeon_get_fp_xres( void ){
  uint32_t xres=(INREG(FP_HORZ_STRETCH)&0x00fff000)>>16;
  xres=(xres+1)*8;
  return xres;
}

/* get flat panel y resolution*/
static uint32_t radeon_get_fp_yres( void ){
  uint32_t yres=(INREG(FP_VERT_STRETCH)&0x00fff000)>>12;
  return yres+1;
}

static void radeon_wait_vsync(void)
{
    int i;

    OUTREG(GEN_INT_STATUS, VSYNC_INT_AK);
    for (i = 0; i < 2000000; i++) 
    {
	if (INREG(GEN_INT_STATUS) & VSYNC_INT) break;
    }
}

#ifdef RAGE128
static void _radeon_engine_idle(void);
static void _radeon_fifo_wait(unsigned);
#define radeon_engine_idle()		_radeon_engine_idle()
#define radeon_fifo_wait(entries)	_radeon_fifo_wait(entries)
/* Flush all dirty data in the Pixel Cache to memory. */
static __inline__ void radeon_engine_flush ( void )
{
    unsigned i;

    OUTREGP(PC_NGUI_CTLSTAT, PC_FLUSH_ALL, ~PC_FLUSH_ALL);
    for (i = 0; i < 2000000; i++) {
	if (!(INREG(PC_NGUI_CTLSTAT) & PC_BUSY)) break;
    }
}

/* Reset graphics card to known state. */
static void radeon_engine_reset( void )
{
    uint32_t clock_cntl_index;
    uint32_t mclk_cntl;
    uint32_t gen_reset_cntl;

    radeon_engine_flush();

    clock_cntl_index = INREG(CLOCK_CNTL_INDEX);
    mclk_cntl        = INPLL(MCLK_CNTL);

    OUTPLL(MCLK_CNTL, mclk_cntl | FORCE_GCP | FORCE_PIPE3D_CP);

    gen_reset_cntl   = INREG(GEN_RESET_CNTL);

    OUTREG(GEN_RESET_CNTL, gen_reset_cntl | SOFT_RESET_GUI);
    INREG(GEN_RESET_CNTL);
    OUTREG(GEN_RESET_CNTL,
	gen_reset_cntl & (uint32_t)(~SOFT_RESET_GUI));
    INREG(GEN_RESET_CNTL);

    OUTPLL(MCLK_CNTL,        mclk_cntl);
    OUTREG(CLOCK_CNTL_INDEX, clock_cntl_index);
    OUTREG(GEN_RESET_CNTL,   gen_reset_cntl);
}
#else

static __inline__ void radeon_engine_flush ( void )
{
	int i;

	/* initiate flush */
	OUTREGP(RB2D_DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL,
	        ~RB2D_DC_FLUSH_ALL);

	for (i=0; i < 2000000; i++) {
		if (!(INREG(RB2D_DSTCACHE_CTLSTAT) & RB2D_DC_BUSY))
			break;
	}
}

static void _radeon_engine_idle(void);
static void _radeon_fifo_wait(unsigned);
#define radeon_engine_idle()		_radeon_engine_idle()
#define radeon_fifo_wait(entries)	_radeon_fifo_wait(entries)

static void radeon_engine_reset( void )
{
	uint32_t clock_cntl_index, mclk_cntl, rbbm_soft_reset;

	radeon_engine_flush ();

	clock_cntl_index = INREG(CLOCK_CNTL_INDEX);
	mclk_cntl = INPLL(MCLK_CNTL);

	OUTPLL(MCLK_CNTL, (mclk_cntl |
			   FORCEON_MCLKA |
			   FORCEON_MCLKB |
			   FORCEON_YCLKA |
			   FORCEON_YCLKB |
			   FORCEON_MC |
			   FORCEON_AIC));
	rbbm_soft_reset = INREG(RBBM_SOFT_RESET);

	OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset |
				SOFT_RESET_CP |
				SOFT_RESET_HI |
				SOFT_RESET_SE |
				SOFT_RESET_RE |
				SOFT_RESET_PP |
				SOFT_RESET_E2 |
				SOFT_RESET_RB |
				SOFT_RESET_HDP);
	INREG(RBBM_SOFT_RESET);
	OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset & (uint32_t)
				~(SOFT_RESET_CP |
				  SOFT_RESET_HI |
				  SOFT_RESET_SE |
				  SOFT_RESET_RE |
				  SOFT_RESET_PP |
				  SOFT_RESET_E2 |
				  SOFT_RESET_RB |
				  SOFT_RESET_HDP));
	INREG(RBBM_SOFT_RESET);

	OUTPLL(MCLK_CNTL, mclk_cntl);
	OUTREG(CLOCK_CNTL_INDEX, clock_cntl_index);
	OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset);

	return;
}
#endif
static void radeon_engine_restore( void )
{
#ifndef RAGE128
    int pitch64;
    uint32_t xres,yres,bpp;
    radeon_fifo_wait(1);
    xres = radeon_get_xres();
    yres = radeon_get_yres();
    bpp = radeon_vid_get_dbpp();
    /* turn of all automatic flushing - we'll do it all */
    OUTREG(RB2D_DSTCACHE_MODE, 0);

    pitch64 = ((xres * (bpp / 8) + 0x3f)) >> 6;

    radeon_fifo_wait(1);
    OUTREG(DEFAULT_OFFSET, (INREG(DEFAULT_OFFSET) & 0xC0000000) |
				  (pitch64 << 22));

    radeon_fifo_wait(1);
#if defined(WORDS_BIGENDIAN)
#ifdef RADEON
    OUTREGP(DP_DATATYPE, HOST_BIG_ENDIAN_EN, ~HOST_BIG_ENDIAN_EN);
#endif
#else
    OUTREGP(DP_DATATYPE, 0, ~HOST_BIG_ENDIAN_EN);
#endif

    radeon_fifo_wait(1);
    OUTREG(DEFAULT_SC_BOTTOM_RIGHT, (DEFAULT_SC_RIGHT_MAX
				    | DEFAULT_SC_BOTTOM_MAX));
    radeon_fifo_wait(1);
    OUTREG(DP_GUI_MASTER_CNTL, (INREG(DP_GUI_MASTER_CNTL)
				       | GMC_BRUSH_SOLID_COLOR
				       | GMC_SRC_DATATYPE_COLOR));

    radeon_fifo_wait(7);
    OUTREG(DST_LINE_START,    0);
    OUTREG(DST_LINE_END,      0);
    OUTREG(DP_BRUSH_FRGD_CLR, 0xffffffff);
    OUTREG(DP_BRUSH_BKGD_CLR, 0x00000000);
    OUTREG(DP_SRC_FRGD_CLR,   0xffffffff);
    OUTREG(DP_SRC_BKGD_CLR,   0x00000000);
    OUTREG(DP_WRITE_MASK,     0xffffffff);

    radeon_engine_idle();
#endif
}
#ifdef RAGE128
static void _radeon_fifo_wait (unsigned entries)
{
    unsigned i;

    for(;;)
    {
	for (i=0; i<2000000; i++)
		if ((INREG(GUI_STAT) & GUI_FIFOCNT_MASK) >= entries)
			return;
	radeon_engine_reset();
	radeon_engine_restore();
    }
}

static void _radeon_engine_idle ( void )
{
    unsigned i;

    /* ensure FIFO is empty before waiting for idle */
    radeon_fifo_wait (64);
    for(;;)
    {
	for (i=0; i<2000000; i++) {
		if ((INREG(GUI_STAT) & GUI_ACTIVE) == 0) {
			radeon_engine_flush ();
			return;
		}
	}
	radeon_engine_reset();
	radeon_engine_restore();
    }
}
#else
static void _radeon_fifo_wait (unsigned entries)
{
    unsigned i;

    for(;;)
    {
	for (i=0; i<2000000; i++)
		if ((INREG(RBBM_STATUS) & RBBM_FIFOCNT_MASK) >= entries)
			return;
	radeon_engine_reset();
	radeon_engine_restore();
    }
}
static void _radeon_engine_idle ( void )
{
    int i;

    /* ensure FIFO is empty before waiting for idle */
    radeon_fifo_wait (64);
    for(;;)
    {
	for (i=0; i<2000000; i++) {
		if (((INREG(RBBM_STATUS) & RBBM_ACTIVE)) == 0) {
			radeon_engine_flush ();
			return;
		}
	}
	radeon_engine_reset();
	radeon_engine_restore();
    }
}
#endif

#ifndef RAGE128
/* Reference color space transform data */
typedef struct tagREF_TRANSFORM
{
	float RefLuma;
	float RefRCb;
	float RefRCr;
	float RefGCb;
	float RefGCr;
	float RefBCb;
	float RefBCr;
} REF_TRANSFORM;

/* Parameters for ITU-R BT.601 and ITU-R BT.709 colour spaces */
REF_TRANSFORM trans[2] =
{
	{1.1678, 0.0, 1.6007, -0.3929, -0.8154, 2.0232, 0.0}, /* BT.601 */
	{1.1678, 0.0, 1.7980, -0.2139, -0.5345, 2.1186, 0.0}  /* BT.709 */
};
/****************************************************************************
 * SetTransform                                                             *
 *  Function: Calculates and sets color space transform from supplied       *
 *            reference transform, gamma, brightness, contrast, hue and     *
 *            saturation.                                                   *
 *    Inputs: bright - brightness                                           *
 *            cont - contrast                                               *
 *            sat - saturation                                              *
 *            hue - hue                                                     *
 *            red_intensity - intense of red component                      *
 *            green_intensity - intense of green component                  *
 *            blue_intensity - intense of blue component                    *
 *            ref - index to the table of refernce transforms               *
 *   Outputs: NONE                                                          *
 ****************************************************************************/

static void radeon_set_transform(float bright, float cont, float sat,
				 float hue, float red_intensity,
				 float green_intensity,float blue_intensity,
				 unsigned ref)
{
	float OvHueSin, OvHueCos;
	float CAdjLuma, CAdjOff;
	float RedAdj,GreenAdj,BlueAdj;
	float CAdjRCb, CAdjRCr;
	float CAdjGCb, CAdjGCr;
	float CAdjBCb, CAdjBCr;
	float OvLuma, OvROff, OvGOff, OvBOff;
	float OvRCb, OvRCr;
	float OvGCb, OvGCr;
	float OvBCb, OvBCr;
	float Loff = 64.0;
	float Coff = 512.0f;

	uint32_t dwOvLuma, dwOvROff, dwOvGOff, dwOvBOff;
	uint32_t dwOvRCb, dwOvRCr;
	uint32_t dwOvGCb, dwOvGCr;
	uint32_t dwOvBCb, dwOvBCr;

	if (ref >= 2) return;

	OvHueSin = sin((double)hue);
	OvHueCos = cos((double)hue);

	CAdjLuma = cont * trans[ref].RefLuma;
	CAdjOff = cont * trans[ref].RefLuma * bright * 1023.0;
	RedAdj = cont * trans[ref].RefLuma * red_intensity * 1023.0;
	GreenAdj = cont * trans[ref].RefLuma * green_intensity * 1023.0;
	BlueAdj = cont * trans[ref].RefLuma * blue_intensity * 1023.0;

	CAdjRCb = sat * -OvHueSin * trans[ref].RefRCr;
	CAdjRCr = sat * OvHueCos * trans[ref].RefRCr;
	CAdjGCb = sat * (OvHueCos * trans[ref].RefGCb - OvHueSin * trans[ref].RefGCr);
	CAdjGCr = sat * (OvHueSin * trans[ref].RefGCb + OvHueCos * trans[ref].RefGCr);
	CAdjBCb = sat * OvHueCos * trans[ref].RefBCb;
	CAdjBCr = sat * OvHueSin * trans[ref].RefBCb;
    
#if 0 /* default constants */
        CAdjLuma = 1.16455078125;

	CAdjRCb = 0.0;
	CAdjRCr = 1.59619140625;
	CAdjGCb = -0.39111328125;
	CAdjGCr = -0.8125;
	CAdjBCb = 2.01708984375;
	CAdjBCr = 0;
#endif
	OvLuma = CAdjLuma;
	OvRCb = CAdjRCb;
	OvRCr = CAdjRCr;
	OvGCb = CAdjGCb;
	OvGCr = CAdjGCr;
	OvBCb = CAdjBCb;
	OvBCr = CAdjBCr;
	OvROff = RedAdj + CAdjOff -
		OvLuma * Loff - (OvRCb + OvRCr) * Coff;
	OvGOff = GreenAdj + CAdjOff - 
		OvLuma * Loff - (OvGCb + OvGCr) * Coff;
	OvBOff = BlueAdj + CAdjOff - 
		OvLuma * Loff - (OvBCb + OvBCr) * Coff;
#if 0 /* default constants */
	OvROff = -888.5;
	OvGOff = 545;
	OvBOff = -1104;
#endif 
   
	dwOvROff = ((int)(OvROff * 2.0)) & 0x1fff;
	dwOvGOff = (int)(OvGOff * 2.0) & 0x1fff;
	dwOvBOff = (int)(OvBOff * 2.0) & 0x1fff;
	/* Whatever docs say about R200 having 3.8 format instead of 3.11
	   as in Radeon is a lie */
#if 0
	if(RadeonFamily == 100)
	{
#endif
		dwOvLuma =(((int)(OvLuma * 2048.0))&0x7fff)<<17;
		dwOvRCb = (((int)(OvRCb * 2048.0))&0x7fff)<<1;
		dwOvRCr = (((int)(OvRCr * 2048.0))&0x7fff)<<17;
		dwOvGCb = (((int)(OvGCb * 2048.0))&0x7fff)<<1;
		dwOvGCr = (((int)(OvGCr * 2048.0))&0x7fff)<<17;
		dwOvBCb = (((int)(OvBCb * 2048.0))&0x7fff)<<1;
		dwOvBCr = (((int)(OvBCr * 2048.0))&0x7fff)<<17;
#if 0
	}
	else
	{
		dwOvLuma = (((int)(OvLuma * 256.0))&0x7ff)<<20;
		dwOvRCb = (((int)(OvRCb * 256.0))&0x7ff)<<4;
		dwOvRCr = (((int)(OvRCr * 256.0))&0x7ff)<<20;
		dwOvGCb = (((int)(OvGCb * 256.0))&0x7ff)<<4;
		dwOvGCr = (((int)(OvGCr * 256.0))&0x7ff)<<20;
		dwOvBCb = (((int)(OvBCb * 256.0))&0x7ff)<<4;
		dwOvBCr = (((int)(OvBCr * 256.0))&0x7ff)<<20;
	}
#endif
	OUTREG(OV0_LIN_TRANS_A, dwOvRCb | dwOvLuma);
	OUTREG(OV0_LIN_TRANS_B, dwOvROff | dwOvRCr);
	OUTREG(OV0_LIN_TRANS_C, dwOvGCb | dwOvLuma);
	OUTREG(OV0_LIN_TRANS_D, dwOvGOff | dwOvGCr);
	OUTREG(OV0_LIN_TRANS_E, dwOvBCb | dwOvLuma);
	OUTREG(OV0_LIN_TRANS_F, dwOvBOff | dwOvBCr);
}

/* Gamma curve definition */
typedef struct 
{
	unsigned int gammaReg;
	unsigned int gammaSlope;
	unsigned int gammaOffset;
}GAMMA_SETTINGS;

/* Recommended gamma curve parameters */
GAMMA_SETTINGS r200_def_gamma[18] = 
{
	{OV0_GAMMA_0_F, 0x100, 0x0000},
	{OV0_GAMMA_10_1F, 0x100, 0x0020},
	{OV0_GAMMA_20_3F, 0x100, 0x0040},
	{OV0_GAMMA_40_7F, 0x100, 0x0080},
	{OV0_GAMMA_80_BF, 0x100, 0x0100},
	{OV0_GAMMA_C0_FF, 0x100, 0x0100},
	{OV0_GAMMA_100_13F, 0x100, 0x0200},
	{OV0_GAMMA_140_17F, 0x100, 0x0200},
	{OV0_GAMMA_180_1BF, 0x100, 0x0300},
	{OV0_GAMMA_1C0_1FF, 0x100, 0x0300},
	{OV0_GAMMA_200_23F, 0x100, 0x0400},
	{OV0_GAMMA_240_27F, 0x100, 0x0400},
	{OV0_GAMMA_280_2BF, 0x100, 0x0500},
	{OV0_GAMMA_2C0_2FF, 0x100, 0x0500},
	{OV0_GAMMA_300_33F, 0x100, 0x0600},
	{OV0_GAMMA_340_37F, 0x100, 0x0600},
	{OV0_GAMMA_380_3BF, 0x100, 0x0700},
	{OV0_GAMMA_3C0_3FF, 0x100, 0x0700}
};

GAMMA_SETTINGS r100_def_gamma[6] = 
{
	{OV0_GAMMA_0_F, 0x100, 0x0000},
	{OV0_GAMMA_10_1F, 0x100, 0x0020},
	{OV0_GAMMA_20_3F, 0x100, 0x0040},
	{OV0_GAMMA_40_7F, 0x100, 0x0080},
	{OV0_GAMMA_380_3BF, 0x100, 0x0100},
	{OV0_GAMMA_3C0_3FF, 0x100, 0x0100}
};

static void make_default_gamma_correction( void )
{
    size_t i;
    if(RadeonFamily == 100) {
	OUTREG(OV0_LIN_TRANS_A, 0x12A00000);
	OUTREG(OV0_LIN_TRANS_B, 0x199018FE);
	OUTREG(OV0_LIN_TRANS_C, 0x12A0F9B0);
	OUTREG(OV0_LIN_TRANS_D, 0xF2F0043B);
	OUTREG(OV0_LIN_TRANS_E, 0x12A02050);
	OUTREG(OV0_LIN_TRANS_F, 0x0000174E);
	for(i=0; i<6; i++){
		OUTREG(r100_def_gamma[i].gammaReg,
		       (r100_def_gamma[i].gammaSlope<<16) |
		        r100_def_gamma[i].gammaOffset);
	}
    }
    else{
	OUTREG(OV0_LIN_TRANS_A, 0x12a00000);
	OUTREG(OV0_LIN_TRANS_B, 0x1990190e);
	OUTREG(OV0_LIN_TRANS_C, 0x12a0f9c0);
	OUTREG(OV0_LIN_TRANS_D, 0xf3000442);
	OUTREG(OV0_LIN_TRANS_E, 0x12a02040);
	OUTREG(OV0_LIN_TRANS_F, 0x175f);

	/* Default Gamma,
	   Of 18 segments for gamma cure, all segments in R200 are programmable,
	   while only lower 4 and upper 2 segments are programmable in Radeon*/
	for(i=0; i<18; i++){
		OUTREG(r200_def_gamma[i].gammaReg,
		       (r200_def_gamma[i].gammaSlope<<16) |
		        r200_def_gamma[i].gammaOffset);
	}
    }
}
#endif
	
static void radeon_vid_make_default(void)
{
#ifdef RAGE128
  OUTREG(OV0_COLOUR_CNTL,0x00101000UL); /* Default brightness and saturation for Rage128 */
#else
  make_default_gamma_correction();
#endif
  besr.deinterlace_pattern = 0x900AAAAA;
  OUTREG(OV0_DEINTERLACE_PATTERN,besr.deinterlace_pattern);
  besr.deinterlace_on=1;
  besr.double_buff=1;
  besr.ckey_on=0;
  besr.graphics_key_msk=0;
  besr.graphics_key_clr=0;
  besr.ckey_cntl = VIDEO_KEY_FN_TRUE|GRAPHIC_KEY_FN_TRUE|CMP_MIX_AND;
}


unsigned vixGetVersion( void ) { return VIDIX_VERSION; }

static unsigned short ati_card_ids[] = 
{
#ifdef RAGE128
 /*
    This driver should be compatible with Rage128 (pro) chips.
    (include adaptive deinterlacing!!!).
    Moreover: the same logic can be used with Mach64 chips.
    (I mean: mach64xx, 3d rage, 3d rage IIc, 3D rage pro, 3d rage mobility).
    but they are incompatible by i/o ports. So if enthusiasts will want
    then they can redefine OUTREG and INREG macros and redefine OV0_*
    constants. Also it seems that mach64 chips supports only: YUY2, YV12, UYVY
    fourccs (422 and 420 formats only).
  */
/* Rage128 Pro GL */
 DEVICE_ATI_RAGE_128_PA_PRO,
 DEVICE_ATI_RAGE_128_PB_PRO,
 DEVICE_ATI_RAGE_128_PC_PRO,
 DEVICE_ATI_RAGE_128_PD_PRO,
 DEVICE_ATI_RAGE_128_PE_PRO,
 DEVICE_ATI_RAGE_128_PF_PRO,
/* Rage128 Pro VR */
 DEVICE_ATI_RAGE_128_PG_PRO,
 DEVICE_ATI_RAGE_128_PH_PRO,
 DEVICE_ATI_RAGE_128_PI_PRO,
 DEVICE_ATI_RAGE_128_PJ_PRO,
 DEVICE_ATI_RAGE_128_PK_PRO,
 DEVICE_ATI_RAGE_128_PL_PRO,
 DEVICE_ATI_RAGE_128_PM_PRO,
 DEVICE_ATI_RAGE_128_PN_PRO,
 DEVICE_ATI_RAGE_128_PO_PRO,
 DEVICE_ATI_RAGE_128_PP_PRO,
 DEVICE_ATI_RAGE_128_PQ_PRO,
 DEVICE_ATI_RAGE_128_PR_PRO,
 DEVICE_ATI_RAGE_128_PS_PRO,
 DEVICE_ATI_RAGE_128_PT_PRO,
 DEVICE_ATI_RAGE_128_PU_PRO,
 DEVICE_ATI_RAGE_128_PV_PRO,
 DEVICE_ATI_RAGE_128_PW_PRO,
 DEVICE_ATI_RAGE_128_PX_PRO,
/* Rage128 GL */
 DEVICE_ATI_RAGE_128_RE_SG,
 DEVICE_ATI_RAGE_128_RF_SG,
 DEVICE_ATI_RAGE_128_RG,
 DEVICE_ATI_RAGE_128_RK_VR,
 DEVICE_ATI_RAGE_128_RL_VR,
 DEVICE_ATI_RAGE_128_SE_4X,
 DEVICE_ATI_RAGE_128_SF_4X,
 DEVICE_ATI_RAGE_128_SG_4X,
 DEVICE_ATI_RAGE_128_SH,
 DEVICE_ATI_RAGE_128_SK_4X,
 DEVICE_ATI_RAGE_128_SL_4X,
 DEVICE_ATI_RAGE_128_SM_4X,
 DEVICE_ATI_RAGE_128_4X,
 DEVICE_ATI_RAGE_128_PRO,
 DEVICE_ATI_RAGE_128_PRO2,
 DEVICE_ATI_RAGE_128_PRO3,
/* these seem to be based on rage 128 instead of mach64 */
 DEVICE_ATI_RAGE_MOBILITY_M3,
 DEVICE_ATI_RAGE_MOBILITY_M32
#else
/* Radeons (indeed: Rage 256 Pro ;) */
 DEVICE_ATI_RADEON_R100_QD,
 DEVICE_ATI_RADEON_R100_QE,
 DEVICE_ATI_RADEON_R100_QF,
 DEVICE_ATI_RADEON_R100_QG,
 DEVICE_ATI_RADEON_VE_QY,
 DEVICE_ATI_RADEON_VE_QZ,
 DEVICE_ATI_RADEON_MOBILITY_M7,
 DEVICE_ATI_RADEON_MOBILITY_M72,
 DEVICE_ATI_RADEON_MOBILITY_M6,
 DEVICE_ATI_RADEON_MOBILITY_M62,
 DEVICE_ATI_RADEON_MOBILITY_U1,
 DEVICE_ATI_RADEON_R200_BB,
 DEVICE_ATI_RADEON_R200_QH,
 DEVICE_ATI_RADEON_R200_QI,
 DEVICE_ATI_RADEON_R200_QJ,
 DEVICE_ATI_RADEON_R200_QK,
 DEVICE_ATI_RADEON_R200_QL,
 DEVICE_ATI_RADEON_R200_QM,
 DEVICE_ATI_RADEON_R200_QH2,
 DEVICE_ATI_RADEON_R200_QI2,
 DEVICE_ATI_RADEON_R200_QJ2,
 DEVICE_ATI_RADEON_R200_QK2,
 DEVICE_ATI_RADEON_RV200_QW,
 DEVICE_ATI_RADEON_RV200_QX,
 DEVICE_ATI_RADEON_R250_ID,
 DEVICE_ATI_RADEON_R250_IE,
 DEVICE_ATI_RADEON_R250_IF,
 DEVICE_ATI_RADEON_R250_IG,
 DEVICE_ATI_RADEON_R250_LD,
 DEVICE_ATI_RADEON_R250_LE,
 DEVICE_ATI_RADEON_R250_LF,
 DEVICE_ATI_RADEON_R250_LG,
 DEVICE_ATI_RV250_5C61_RADEON,
 DEVICE_ATI_RV250_5C63_RADEON,
 DEVICE_ATI_RV280_RADEON_9200,
 DEVICE_ATI_RV280_RADEON_92002,
 DEVICE_ATI_RV280_RADEON_92003,
 DEVICE_ATI_RV280_RADEON_92004,
 DEVICE_ATI_RV280_RADEON_92005,
 DEVICE_ATI_RADEON_R300_ND,
 DEVICE_ATI_RADEON_R300_NE,
 DEVICE_ATI_RADEON_R300_NF,
 DEVICE_ATI_RADEON_R300_NG,
 DEVICE_ATI_RADEON_R300_AE,
 DEVICE_ATI_RADEON_R300_AF,
 DEVICE_ATI_RADEON_RV350_AP,
 DEVICE_ATI_RADEON_RV350_AR,
 DEVICE_ATI_RADEON_R350_AH,
 DEVICE_ATI_RADEON_R350_AI,
 DEVICE_ATI_RADEON_R350_NH,
 DEVICE_ATI_RADEON_R360_NJ,
 DEVICE_ATI_RV350_MOBILITY_RADEON,
 DEVICE_ATI_RV350_MOBILITY_RADEON2
#endif
};

static int find_chip(unsigned chip_id)
{
  unsigned i;
  for(i = 0;i < sizeof(ati_card_ids)/sizeof(unsigned short);i++)
  {
    if(chip_id == ati_card_ids[i]) return i;
  }
  return -1;
}

static pciinfo_t pci_info;
static int probed=0;

vidix_capability_t def_cap = 
{
#ifdef RAGE128
    "BES driver for Rage128 cards",
#else
    "BES driver for Radeon cards",
#endif
    "Nick Kurshev",
    TYPE_OUTPUT | TYPE_FX,
    { 0, 0, 0, 0 },
    2048,
    2048,
    4,
    4,
    -1,
    FLAG_UPSCALER | FLAG_DOWNSCALER | FLAG_EQUALIZER,
    VENDOR_ATI,
    0,
    { 0, 0, 0, 0}
};

#ifdef HAVE_X11
void probe_fireGL_driver() {
  Display *dp = XOpenDisplay ((void*)0);
  int n = 0;
  char **extlist;
  if (dp==NULL) {
       return;
  }
  extlist = XListExtensions (dp, &n);
  XCloseDisplay (dp);
  if (extlist) {
    int i;
    int ext_fgl = 0, ext_fglrx = 0;
    for (i = 0; i < n; i++) {
      if (!strcmp(extlist[i], "ATIFGLEXTENSION")) ext_fgl = 1;
      if (!strcmp(extlist[i], "ATIFGLRXDRI")) ext_fglrx = 1;
    }
    if (ext_fgl) {
      printf(RADEON_MSG" ATI FireGl driver detected");
      firegl_shift = 0x500000;
      if (!ext_fglrx) {
        printf(", but DRI seems not to be activated\n");
        printf(RADEON_MSG" Output may not work correctly, check your DRI configuration!");
      }
      printf("\n");
    }
  }
}
#endif

int vixProbe( int verbose,int force )
{
  pciinfo_t lst[MAX_PCI_DEVICES];
  unsigned i,num_pci;
  int err;
  __verbose = verbose;
  err = pci_scan(lst,&num_pci);
  if(err)
  {
    printf(RADEON_MSG" Error occurred during pci scan: %s\n",strerror(err));
    return err;
  }
  else
  {
    err = ENXIO;
    for(i=0;i<num_pci;i++)
    {
      if(lst[i].vendor == VENDOR_ATI)
      {
        int idx;
	const char *dname;
	idx = find_chip(lst[i].device);
	if(idx == -1 && force == PROBE_NORMAL) continue;
	dname = pci_device_name(VENDOR_ATI,lst[i].device);
	dname = dname ? dname : "Unknown chip";
	printf(RADEON_MSG" Found chip: %s\n",dname);
	if ((lst[i].command & PCI_COMMAND_IO) == 0)
	{
		printf("[radeon] Device is disabled, ignoring\n");
		continue;
	}
#ifndef RAGE128	
	if(idx != -1)
#ifdef HAVE_X11
	probe_fireGL_driver();
#endif
	{
          switch(ati_card_ids[idx]) {
            /* Original radeon */
            case DEVICE_ATI_RADEON_R100_QD:
            case DEVICE_ATI_RADEON_R100_QE:
            case DEVICE_ATI_RADEON_R100_QF:
            case DEVICE_ATI_RADEON_R100_QG:
              RadeonFamily = 100;
              break;
              
            /* Radeon VE / Radeon Mobility */
            case DEVICE_ATI_RADEON_VE_QY:
            case DEVICE_ATI_RADEON_VE_QZ:
            case DEVICE_ATI_RADEON_MOBILITY_M6:
            case DEVICE_ATI_RADEON_MOBILITY_M62:
	    case DEVICE_ATI_RADEON_MOBILITY_U1:
              RadeonFamily = 120;
              break;
              
            /* Radeon 7500 / Radeon Mobility 7500 */
            case DEVICE_ATI_RADEON_RV200_QW:
            case DEVICE_ATI_RADEON_RV200_QX: 
            case DEVICE_ATI_RADEON_MOBILITY_M7:
            case DEVICE_ATI_RADEON_MOBILITY_M72:
              RadeonFamily = 150;
              break;
              
            /* Radeon 8500 */
            case DEVICE_ATI_RADEON_R200_BB:
            case DEVICE_ATI_RADEON_R200_QH:
            case DEVICE_ATI_RADEON_R200_QI:
            case DEVICE_ATI_RADEON_R200_QJ:
            case DEVICE_ATI_RADEON_R200_QK:
            case DEVICE_ATI_RADEON_R200_QL:
            case DEVICE_ATI_RADEON_R200_QM:
            case DEVICE_ATI_RADEON_R200_QH2:
            case DEVICE_ATI_RADEON_R200_QI2:
            case DEVICE_ATI_RADEON_R200_QJ2:
            case DEVICE_ATI_RADEON_R200_QK2:
              RadeonFamily = 200;
              break;
              
            /* Radeon 9000 */
            case DEVICE_ATI_RADEON_R250_ID:
            case DEVICE_ATI_RADEON_R250_IE:
            case DEVICE_ATI_RADEON_R250_IF:
            case DEVICE_ATI_RADEON_R250_IG:
            case DEVICE_ATI_RADEON_R250_LD:
            case DEVICE_ATI_RADEON_R250_LE:
            case DEVICE_ATI_RADEON_R250_LF:
            case DEVICE_ATI_RADEON_R250_LG:
            case DEVICE_ATI_RV250_5C61_RADEON:
            case DEVICE_ATI_RV250_5C63_RADEON:
              RadeonFamily = 250;
              break;
              
            /* Radeon 9200 */
            case DEVICE_ATI_RV280_RADEON_9200:
            case DEVICE_ATI_RV280_RADEON_92002:
            case DEVICE_ATI_RV280_RADEON_92003:
            case DEVICE_ATI_RV280_RADEON_92004:
            case DEVICE_ATI_RV280_RADEON_92005:
              RadeonFamily = 280;
              break;

            /* Radeon 9700 */
            case DEVICE_ATI_RADEON_R300_ND:
            case DEVICE_ATI_RADEON_R300_NE:
            case DEVICE_ATI_RADEON_R300_NF:
            case DEVICE_ATI_RADEON_R300_NG:
            case DEVICE_ATI_RADEON_R300_AE:
            case DEVICE_ATI_RADEON_R300_AF:
              RadeonFamily = 300;
              break;

            /* Radeon 9600/9800 */
            case DEVICE_ATI_RADEON_RV350_AP:
            case DEVICE_ATI_RADEON_RV350_AR:
            case DEVICE_ATI_RADEON_R350_NH:
            case DEVICE_ATI_RADEON_R350_AH:
            case DEVICE_ATI_RADEON_R350_AI:
            case DEVICE_ATI_RADEON_R360_NJ:
            case DEVICE_ATI_RV350_MOBILITY_RADEON:
            case DEVICE_ATI_RV350_MOBILITY_RADEON2:
              RadeonFamily = 350;
              break;

            default:
              break;
          }
	}
#endif
	if(force > PROBE_NORMAL)
	{
	    printf(RADEON_MSG" Driver was forced. Was found %sknown chip\n",idx == -1 ? "un" : "");
	    if(idx == -1)
#ifdef RAGE128
		printf(RADEON_MSG" Assuming it as Rage128\n");
#else
		printf(RADEON_MSG" Assuming it as Radeon1\n");
#endif
	}
	def_cap.device_id = lst[i].device;
	err = 0;
	memcpy(&pci_info,&lst[i],sizeof(pciinfo_t));
	probed=1;
	break;
      }
    }
  }
  if(err && verbose) printf(RADEON_MSG" Can't find chip\n");
  return err;
}

static void radeon_vid_dump_regs( void ); /* forward declaration */

int vixInit( void )
{
  int err;
  if(!probed) 
  {
    printf(RADEON_MSG" Driver was not probed but is being initializing\n");
    return EINTR;
  }    
  if((radeon_mmio_base = map_phys_mem(pci_info.base2,0xFFFF))==(void *)-1) return ENOMEM;
  radeon_ram_size = INREG(CONFIG_MEMSIZE);
  /* mem size is bits [28:0], mask off the rest. Range: from 1Mb up to 512 Mb */
  radeon_ram_size &=  CONFIG_MEMSIZE_MASK;
#ifdef RADEON
  /* according to XFree86 4.2.0, some production M6's return 0 for 8MB */
  if (radeon_ram_size == 0 &&
      (def_cap.device_id == DEVICE_ATI_RADEON_MOBILITY_M6 ||
       def_cap.device_id == DEVICE_ATI_RADEON_MOBILITY_M62))
  {
      printf(RADEON_MSG" Workarounding buggy Radeon Mobility M6 (0 vs. 8MB ram)\n");
      radeon_ram_size = 8192*1024;
  }
#else
  /* Rage Mobility (rage128) also has memsize bug */
  if (radeon_ram_size == 0 &&
      (def_cap.device_id == DEVICE_ATI_RAGE_MOBILITY_M3 ||
       def_cap.device_id == DEVICE_ATI_RAGE_MOBILITY_M32))
  {
      printf(RADEON_MSG" Workarounding buggy Rage Mobility M3 (0 vs. 8MB ram)\n");
      radeon_ram_size = 8192*1024;
  }
#endif
#ifdef WIN32
  if(radeon_ram_size > 16*1024*1024)radeon_ram_size=16*1024*1024;
#endif
  if((radeon_mem_base = map_phys_mem(pci_info.base0,radeon_ram_size))==(void *)-1) return ENOMEM;
  memset(&besr,0,sizeof(bes_registers_t));
  radeon_vid_make_default();
  printf(RADEON_MSG" Video memory = %uMb\n",radeon_ram_size/0x100000);
  err = mtrr_set_type(pci_info.base0,radeon_ram_size,MTRR_TYPE_WRCOMB);
  if(!err) printf(RADEON_MSG" Set write-combining type of video memory\n");

  radeon_fifo_wait(3);
  SAVED_OV0_GRAPHICS_KEY_CLR = INREG(OV0_GRAPHICS_KEY_CLR);
  SAVED_OV0_GRAPHICS_KEY_MSK = INREG(OV0_GRAPHICS_KEY_MSK);
  SAVED_OV0_VID_KEY_CLR = INREG(OV0_VID_KEY_CLR);
  SAVED_OV0_VID_KEY_MSK = INREG(OV0_VID_KEY_MSK);
  SAVED_OV0_KEY_CNTL = INREG(OV0_KEY_CNTL);
  printf(RADEON_MSG" Saved overlay colorkey settings\n");

#ifdef RADEON
  switch(RadeonFamily)
    {
    case 100:
    case 120:
    case 150:
    case 250:
    case 280:
      is_shift_required=1;
      break;
    default:
      break;
    }
#endif

/* XXX: hack, but it works for me (tm) */
#ifdef WORDS_BIGENDIAN
#if defined(RAGE128) 
    /* code from gatos */
    {
	SAVED_CONFIG_CNTL = INREG(CONFIG_CNTL);
	OUTREG(CONFIG_CNTL, SAVED_CONFIG_CNTL &
	    ~(APER_0_BIG_ENDIAN_16BPP_SWAP|APER_0_BIG_ENDIAN_32BPP_SWAP));
	    
//	printf("saved: %x, current: %x\n", SAVED_CONFIG_CNTL,
//	    INREG(CONFIG_CNTL));
    }
#else
    /*code from radeon_video.c*/
    {
    	SAVED_CONFIG_CNTL = INREG(RADEON_SURFACE_CNTL);
/*	OUTREG(RADEON_SURFACE_CNTL, (SAVED_CONFIG_CNTL |
		RADEON_NONSURF_AP0_SWP_32BPP) & ~RADEON_NONSURF_AP0_SWP_16BPP);
*/
	OUTREG(RADEON_SURFACE_CNTL, SAVED_CONFIG_CNTL & ~(RADEON_NONSURF_AP0_SWP_32BPP
						   | RADEON_NONSURF_AP0_SWP_16BPP));

/*
	OUTREG(RADEON_SURFACE_CNTL, (SAVED_CONFIG_CNTL | RADEON_NONSURF_AP0_SWP_32BPP)
				    & ~RADEON_NONSURF_AP0_SWP_16BPP);
*/
    }
#endif
#endif

  if(__verbose > 1) radeon_vid_dump_regs();
  return 0;  
}

void vixDestroy( void )
{
  /* remove colorkeying */
  radeon_fifo_wait(3);
  OUTREG(OV0_GRAPHICS_KEY_CLR, SAVED_OV0_GRAPHICS_KEY_CLR);
  OUTREG(OV0_GRAPHICS_KEY_MSK, SAVED_OV0_GRAPHICS_KEY_MSK);
  OUTREG(OV0_VID_KEY_CLR, SAVED_OV0_VID_KEY_CLR);
  OUTREG(OV0_VID_KEY_MSK, SAVED_OV0_VID_KEY_MSK);
  OUTREG(OV0_KEY_CNTL, SAVED_OV0_KEY_CNTL);
  printf(RADEON_MSG" Restored overlay colorkey settings\n");

#ifdef WORDS_BIGENDIAN
#if defined(RAGE128)
    OUTREG(CONFIG_CNTL, SAVED_CONFIG_CNTL);
//    printf("saved: %x, restored: %x\n", SAVED_CONFIG_CNTL,
//	INREG(CONFIG_CNTL));
#else
    OUTREG(RADEON_SURFACE_CNTL, SAVED_CONFIG_CNTL);
#endif
#endif

  unmap_phys_mem(radeon_mem_base,radeon_ram_size);
  unmap_phys_mem(radeon_mmio_base,0xFFFF);
}

int vixGetCapability(vidix_capability_t *to)
{
  memcpy(to,&def_cap,sizeof(vidix_capability_t));
  return 0; 
}

/*
  Full list of fourcc which are supported by Win2K redeon driver:
  YUY2, UYVY, DDES, OGLT, OGL2, OGLS, OGLB, OGNT, OGNZ, OGNS,
  IF09, YVU9, IMC4, M2IA, IYUV, VBID, DXT1, DXT2, DXT3, DXT4, DXT5
*/
uint32_t supported_fourcc[] = 
{
  IMGFMT_Y800, IMGFMT_Y8, IMGFMT_YVU9, IMGFMT_IF09,
  IMGFMT_YV12, IMGFMT_I420, IMGFMT_IYUV, 
  IMGFMT_UYVY, IMGFMT_YUY2, IMGFMT_YVYU,
  IMGFMT_RGB15, IMGFMT_BGR15,
  IMGFMT_RGB16, IMGFMT_BGR16,
  IMGFMT_RGB32, IMGFMT_BGR32
};

inline static int is_supported_fourcc(uint32_t fourcc)
{
  unsigned int i;
  for(i=0;i<sizeof(supported_fourcc)/sizeof(uint32_t);i++)
  {
    if(fourcc==supported_fourcc[i]) return 1;
  }
  return 0;
}

int vixQueryFourcc(vidix_fourcc_t *to)
{
    if(is_supported_fourcc(to->fourcc))
    {
	to->depth = VID_DEPTH_1BPP | VID_DEPTH_2BPP |
		    VID_DEPTH_4BPP | VID_DEPTH_8BPP |
		    VID_DEPTH_12BPP| VID_DEPTH_15BPP|
		    VID_DEPTH_16BPP| VID_DEPTH_24BPP|
		    VID_DEPTH_32BPP;
	to->flags = VID_CAP_EXPAND | VID_CAP_SHRINK | VID_CAP_COLORKEY;
	return 0;
    }
    else  to->depth = to->flags = 0;
    return ENOSYS;
}

static void radeon_vid_dump_regs( void )
{
  size_t i;
  printf(RADEON_MSG"*** Begin of DRIVER variables dump ***\n");
  printf(RADEON_MSG"radeon_mmio_base=%p\n",radeon_mmio_base);
  printf(RADEON_MSG"radeon_mem_base=%p\n",radeon_mem_base);
  printf(RADEON_MSG"radeon_overlay_off=%08X\n",radeon_overlay_off);
  printf(RADEON_MSG"radeon_ram_size=%08X\n",radeon_ram_size);
  printf(RADEON_MSG"video mode: %ux%u@%u\n",radeon_get_xres(),radeon_get_yres(),radeon_vid_get_dbpp());
  printf(RADEON_MSG"flatpanel size: %ux%u\n",radeon_get_fp_xres(),radeon_get_fp_yres());
  printf(RADEON_MSG"*** Begin of OV0 registers dump ***\n");
  for(i=0;i<sizeof(vregs)/sizeof(video_registers_t);i++)
	printf(RADEON_MSG"%s = %08X\n",vregs[i].sname,INREG(vregs[i].name));
  printf(RADEON_MSG"*** End of OV0 registers dump ***\n");
}

static void radeon_vid_stop_video( void )
{
    radeon_engine_idle();
    OUTREG(OV0_SCALE_CNTL, SCALER_SOFT_RESET);
    OUTREG(OV0_EXCLUSIVE_HORZ, 0);
    OUTREG(OV0_AUTO_FLIP_CNTL, 0);   /* maybe */
    OUTREG(OV0_FILTER_CNTL, FILTER_HARDCODED_COEF);
#ifdef RADEON
    OUTREG(OV0_KEY_CNTL, GRAPHIC_KEY_FN_EQ);
#else
    OUTREG(OV0_KEY_CNTL, GRAPHIC_KEY_FN_NE);
#endif
    OUTREG(OV0_TEST, 0);
}

static void radeon_vid_display_video( void )
{
    int bes_flags;
    /** workaround for Xorg-6.8 not saving the surface registers on bigendian architectures */
#ifdef WORDS_BIGENDIAN
#if defined(RAGE128) 
    /* code from gatos */
    {
	SAVED_CONFIG_CNTL = INREG(CONFIG_CNTL);
	OUTREG(CONFIG_CNTL, SAVED_CONFIG_CNTL &
	    ~(APER_0_BIG_ENDIAN_16BPP_SWAP|APER_0_BIG_ENDIAN_32BPP_SWAP));
	    
//	printf("saved: %x, current: %x\n", SAVED_CONFIG_CNTL,
//	    INREG(CONFIG_CNTL));
    }
#else
    /*code from radeon_video.c*/
    {
    	SAVED_CONFIG_CNTL = INREG(RADEON_SURFACE_CNTL);
/*	OUTREG(RADEON_SURFACE_CNTL, (SAVED_CONFIG_CNTL |
		RADEON_NONSURF_AP0_SWP_32BPP) & ~RADEON_NONSURF_AP0_SWP_16BPP);
*/
	OUTREG(RADEON_SURFACE_CNTL, SAVED_CONFIG_CNTL & ~(RADEON_NONSURF_AP0_SWP_32BPP
						   | RADEON_NONSURF_AP0_SWP_16BPP));

/*
	OUTREG(RADEON_SURFACE_CNTL, (SAVED_CONFIG_CNTL | RADEON_NONSURF_AP0_SWP_32BPP)
				    & ~RADEON_NONSURF_AP0_SWP_16BPP);
*/
    }
#endif
#endif


 
    radeon_fifo_wait(2);
    OUTREG(OV0_REG_LOAD_CNTL,		REG_LD_CTL_LOCK);
    radeon_engine_idle();
    while(!(INREG(OV0_REG_LOAD_CNTL)&REG_LD_CTL_LOCK_READBACK));
    radeon_fifo_wait(15);

    /* Shutdown capturing */
    OUTREG(FCP_CNTL, FCP_CNTL__GND);
    OUTREG(CAP0_TRIG_CNTL, 0);

    OUTREG(VID_BUFFER_CONTROL, (1<<16) | 0x01);
    OUTREG(DISP_TEST_DEBUG_CNTL, 0);

    OUTREG(OV0_AUTO_FLIP_CNTL,OV0_AUTO_FLIP_CNTL_SOFT_BUF_ODD);

    if(besr.deinterlace_on) OUTREG(OV0_DEINTERLACE_PATTERN,besr.deinterlace_pattern);
#ifdef RAGE128
    OUTREG(OV0_COLOUR_CNTL, (((besr.brightness*64)/1000) & 0x7f) |
                            (((besr.saturation*31+31000)/2000) << 8) |
                            (((besr.saturation*31+31000)/2000) << 16));
#endif
    radeon_fifo_wait(2);
    OUTREG(OV0_GRAPHICS_KEY_MSK, besr.graphics_key_msk);
    OUTREG(OV0_GRAPHICS_KEY_CLR, besr.graphics_key_clr);
    OUTREG(OV0_KEY_CNTL,besr.ckey_cntl);

    OUTREG(OV0_H_INC,			besr.h_inc);
    OUTREG(OV0_STEP_BY,			besr.step_by);
    OUTREG(OV0_Y_X_START,		besr.y_x_start);
    OUTREG(OV0_Y_X_END,			besr.y_x_end);
    OUTREG(OV0_V_INC,			besr.v_inc);
    OUTREG(OV0_P1_BLANK_LINES_AT_TOP,	besr.p1_blank_lines_at_top);
    OUTREG(OV0_P23_BLANK_LINES_AT_TOP,	besr.p23_blank_lines_at_top);
    OUTREG(OV0_VID_BUF_PITCH0_VALUE,	besr.vid_buf_pitch0_value);
    OUTREG(OV0_VID_BUF_PITCH1_VALUE,	besr.vid_buf_pitch1_value);
    OUTREG(OV0_P1_X_START_END,		besr.p1_x_start_end);
    OUTREG(OV0_P2_X_START_END,		besr.p2_x_start_end);
    OUTREG(OV0_P3_X_START_END,		besr.p3_x_start_end);
#ifdef RADEON
    OUTREG(OV0_BASE_ADDR,		besr.base_addr);
#endif
    OUTREG(OV0_VID_BUF0_BASE_ADRS,	besr.vid_buf_base_adrs_y[0]);
    OUTREG(OV0_VID_BUF1_BASE_ADRS,	besr.vid_buf_base_adrs_v[0]);
    OUTREG(OV0_VID_BUF2_BASE_ADRS,	besr.vid_buf_base_adrs_u[0]);
    radeon_fifo_wait(9);
    OUTREG(OV0_VID_BUF3_BASE_ADRS,	besr.vid_buf_base_adrs_y[0]);
    OUTREG(OV0_VID_BUF4_BASE_ADRS,	besr.vid_buf_base_adrs_v[0]);
    OUTREG(OV0_VID_BUF5_BASE_ADRS,	besr.vid_buf_base_adrs_u[0]);
    OUTREG(OV0_P1_V_ACCUM_INIT,		besr.p1_v_accum_init);
    OUTREG(OV0_P1_H_ACCUM_INIT,		besr.p1_h_accum_init);
    OUTREG(OV0_P23_H_ACCUM_INIT,	besr.p23_h_accum_init);
    OUTREG(OV0_P23_V_ACCUM_INIT,	besr.p23_v_accum_init);

#ifdef RADEON
    bes_flags = SCALER_ENABLE |
                SCALER_SMART_SWITCH;
//		SCALER_HORZ_PICK_NEAREST |
//		SCALER_VERT_PICK_NEAREST |
#endif
    bes_flags = SCALER_ENABLE |
                SCALER_SMART_SWITCH |
		SCALER_Y2R_TEMP |
		SCALER_PIX_EXPAND;
    if(besr.double_buff) bes_flags |= SCALER_DOUBLE_BUFFER;
    if(besr.deinterlace_on) bes_flags |= SCALER_ADAPTIVE_DEINT;
#ifdef RAGE128
    bes_flags |= SCALER_BURST_PER_PLANE;
#endif
    switch(besr.fourcc)
    {
        case IMGFMT_RGB15:
        case IMGFMT_BGR15: bes_flags |= SCALER_SOURCE_15BPP; break;
	case IMGFMT_RGB16:
	case IMGFMT_BGR16: bes_flags |= SCALER_SOURCE_16BPP; break;
/*
        case IMGFMT_RGB24:
        case IMGFMT_BGR24: bes_flags |= SCALER_SOURCE_24BPP; break;
*/
        case IMGFMT_RGB32:
	case IMGFMT_BGR32: bes_flags |= SCALER_SOURCE_32BPP; break;
        /* 4:1:0 */
	case IMGFMT_IF09:
        case IMGFMT_YVU9:  bes_flags |= SCALER_SOURCE_YUV9; break;
	/* 4:0:0 */
	case IMGFMT_Y800:
	case IMGFMT_Y8:
        /* 4:2:0 */
	case IMGFMT_IYUV:
	case IMGFMT_I420:
	case IMGFMT_YV12:  bes_flags |= SCALER_SOURCE_YUV12; break;
        /* 4:2:2 */
        case IMGFMT_YVYU:
	case IMGFMT_UYVY:  bes_flags |= SCALER_SOURCE_YVYU422; break;
	case IMGFMT_YUY2:
	default:           bes_flags |= SCALER_SOURCE_VYUY422; break;
    }
    OUTREG(OV0_SCALE_CNTL,		bes_flags);
    OUTREG(OV0_REG_LOAD_CNTL,		0);
    if(__verbose > 1) printf(RADEON_MSG"we wanted: scaler=%08X\n",bes_flags);
    if(__verbose > 1) radeon_vid_dump_regs();
}

static unsigned radeon_query_pitch(unsigned fourcc,const vidix_yuv_t *spitch)
{
  unsigned pitch,spy,spv,spu;
  spy = spv = spu = 0;
  switch(spitch->y)
  {
    case 16:
    case 32:
    case 64:
    case 128:
    case 256: spy = spitch->y; break;
    default: break;
  }
  switch(spitch->u)
  {
    case 16:
    case 32:
    case 64:
    case 128:
    case 256: spu = spitch->u; break;
    default: break;
  }
  switch(spitch->v)
  {
    case 16:
    case 32:
    case 64:
    case 128:
    case 256: spv = spitch->v; break;
    default: break;
  }
  switch(fourcc)
  {
	/* 4:2:0 */
	case IMGFMT_IYUV:
	case IMGFMT_YV12:
	case IMGFMT_I420:
		if(spy > 16 && spu == spy/2 && spv == spy/2)	pitch = spy;
		else						pitch = 32;
		break;
	/* 4:1:0 */
	case IMGFMT_IF09:
	case IMGFMT_YVU9:
		if(spy > 32 && spu == spy/4 && spv == spy/4)	pitch = spy;
		else						pitch = 64;
		break;
	default:
		if(spy >= 16)	pitch = spy;
		else		pitch = 16;
		break;
  }
  return pitch;
}

static int radeon_vid_init_video( vidix_playback_t *config )
{
    uint32_t i,tmp,src_w,src_h,dest_w,dest_h,pitch,h_inc,step_by,left,leftUV,top;
    int is_400,is_410,is_420,is_rgb32,is_rgb,best_pitch,mpitch;
    radeon_vid_stop_video();
    left = config->src.x << 16;
    top =  config->src.y << 16;
    src_h = config->src.h;
    src_w = config->src.w;
    is_400 = is_410 = is_420 = is_rgb32 = is_rgb = 0;
    if(config->fourcc == IMGFMT_YV12 ||
       config->fourcc == IMGFMT_I420 ||
       config->fourcc == IMGFMT_IYUV) is_420 = 1;
    if(config->fourcc == IMGFMT_YVU9 ||
       config->fourcc == IMGFMT_IF09) is_410 = 1;
    if(config->fourcc == IMGFMT_Y800 ||
       config->fourcc == IMGFMT_Y8) is_400 = 1;
    if(config->fourcc == IMGFMT_RGB32 ||
       config->fourcc == IMGFMT_BGR32) is_rgb32 = 1;
    if(config->fourcc == IMGFMT_RGB32 ||
       config->fourcc == IMGFMT_BGR32 ||
       config->fourcc == IMGFMT_RGB24 ||
       config->fourcc == IMGFMT_BGR24 ||
       config->fourcc == IMGFMT_RGB16 ||
       config->fourcc == IMGFMT_BGR16 ||
       config->fourcc == IMGFMT_RGB15 ||
       config->fourcc == IMGFMT_BGR15) is_rgb = 1;
    best_pitch = radeon_query_pitch(config->fourcc,&config->src.pitch);
    mpitch = best_pitch-1;
    switch(config->fourcc)
    {
	/* 4:0:0 */
	case IMGFMT_Y800:
	case IMGFMT_Y8:
	/* 4:1:0 */
	case IMGFMT_YVU9:
	case IMGFMT_IF09:
	/* 4:2:0 */
	case IMGFMT_IYUV:
	case IMGFMT_YV12:
	case IMGFMT_I420: pitch = (src_w + mpitch) & ~mpitch;
			  config->dest.pitch.y = 
			  config->dest.pitch.u = 
			  config->dest.pitch.v = best_pitch;
			  break;
	/* RGB 4:4:4:4 */
	case IMGFMT_RGB32:
	case IMGFMT_BGR32: pitch = (src_w*4 + mpitch) & ~mpitch;
			  config->dest.pitch.y = 
			  config->dest.pitch.u = 
			  config->dest.pitch.v = best_pitch;
			  break;
	/* 4:2:2 */
        default: /* RGB15, RGB16, YVYU, UYVY, YUY2 */
			  pitch = ((src_w*2) + mpitch) & ~mpitch;
			  config->dest.pitch.y =
			  config->dest.pitch.u =
			  config->dest.pitch.v = best_pitch;
			  break;
    }
    dest_w = config->dest.w;
    dest_h = config->dest.h;
    if(radeon_is_dbl_scan()) dest_h *= 2;
    besr.dest_bpp = radeon_vid_get_dbpp();
    besr.fourcc = config->fourcc;

    /* flat panel */
    if(INREG(FP_VERT_STRETCH)&VERT_STRETCH_ENABLE){
      besr.v_inc = (src_h * radeon_get_yres() / radeon_get_fp_yres() << 20) / dest_h;
    }
    else besr.v_inc = (src_h << 20) / dest_h;
    if(radeon_is_interlace()) besr.v_inc *= 2;
    h_inc = (src_w << 12) / dest_w;

    {
        unsigned int ecp_div;
        ecp_div = (INPLL(VCLK_ECP_CNTL) >> 8) & 3;
        h_inc <<= ecp_div;
    }


    step_by = 1;
    while(h_inc >= (2 << 12)) {
	step_by++;
	h_inc >>= 1;
    }

    /* keep everything in 16.16 */
    besr.base_addr = INREG(DISPLAY_BASE_ADDR);
    config->offsets[0] = 0;
    for(i=1;i<besr.vid_nbufs;i++)
	    config->offsets[i] = config->offsets[i-1]+config->frame_size;
    if(is_420 || is_410 || is_400)
    {
        uint32_t d1line,d2line,d3line;
	d1line = top*pitch;
	if(is_420)
	{
	    d2line = src_h*pitch+(d1line>>2);
	    d3line = d2line+((src_h*pitch)>>2);
	}
	else
	if(is_410)
	{
	    d2line = src_h*pitch+(d1line>>4);
	    d3line = d2line+((src_h*pitch)>>4);
	}
	else
	{
	    d2line = 0;
	    d3line = 0;
	}
	d1line += (left >> 16) & ~15;
	if(is_420)
	{
	    d2line += (left >> 17) & ~15;
	    d3line += (left >> 17) & ~15;
	}
	else
	if(is_410)
	{
	    d2line += (left >> 18) & ~15;
	    d3line += (left >> 18) & ~15;
	}
	config->offset.y = d1line & VIF_BUF0_BASE_ADRS_MASK;
	if(is_400)
	{
	    config->offset.v = 0;
	    config->offset.u = 0;
	}
	else
	{
	    config->offset.v = d2line & VIF_BUF1_BASE_ADRS_MASK;
	    config->offset.u = d3line & VIF_BUF2_BASE_ADRS_MASK;
	}
	for(i=0;i<besr.vid_nbufs;i++)
	{
	    besr.vid_buf_base_adrs_y[i]=((radeon_overlay_off+config->offsets[i]+config->offset.y)&VIF_BUF0_BASE_ADRS_MASK);
	    if(is_400)
	    {
		besr.vid_buf_base_adrs_v[i]=0;
		besr.vid_buf_base_adrs_u[i]=0;
	    }
	    else
	    {
		if (besr.fourcc == IMGFMT_I420 || besr.fourcc == IMGFMT_IYUV)
		{
		    besr.vid_buf_base_adrs_u[i]=((radeon_overlay_off+config->offsets[i]+config->offset.v)&VIF_BUF1_BASE_ADRS_MASK)|VIF_BUF1_PITCH_SEL;
		    besr.vid_buf_base_adrs_v[i]=((radeon_overlay_off+config->offsets[i]+config->offset.u)&VIF_BUF2_BASE_ADRS_MASK)|VIF_BUF2_PITCH_SEL;
		}
		else
		{
		    besr.vid_buf_base_adrs_v[i]=((radeon_overlay_off+config->offsets[i]+config->offset.v)&VIF_BUF1_BASE_ADRS_MASK)|VIF_BUF1_PITCH_SEL;
		    besr.vid_buf_base_adrs_u[i]=((radeon_overlay_off+config->offsets[i]+config->offset.u)&VIF_BUF2_BASE_ADRS_MASK)|VIF_BUF2_PITCH_SEL;
		}
	    }
	}
	config->offset.y = ((besr.vid_buf_base_adrs_y[0])&VIF_BUF0_BASE_ADRS_MASK) - radeon_overlay_off;
	if(is_400)
	{
	    config->offset.v = 0;
	    config->offset.u = 0;
	}
	else
	{
	    config->offset.v = ((besr.vid_buf_base_adrs_v[0])&VIF_BUF1_BASE_ADRS_MASK) - radeon_overlay_off;
	    config->offset.u = ((besr.vid_buf_base_adrs_u[0])&VIF_BUF2_BASE_ADRS_MASK) - radeon_overlay_off;
	}
    }
    else
    {
      config->offset.y = config->offset.u = config->offset.v = ((left & ~7) << 1)&VIF_BUF0_BASE_ADRS_MASK;
      for(i=0;i<besr.vid_nbufs;i++)
      {
	besr.vid_buf_base_adrs_y[i] =
	besr.vid_buf_base_adrs_u[i] =
	besr.vid_buf_base_adrs_v[i] = radeon_overlay_off + config->offsets[i] + config->offset.y;
      }
    }

    tmp = (left & 0x0003ffff) + 0x00028000 + (h_inc << 3);
    besr.p1_h_accum_init = ((tmp <<  4) & 0x000f8000) |
			   ((tmp << 12) & 0xf0000000);

    tmp = ((left >> 1) & 0x0001ffff) + 0x00028000 + (h_inc << 2);
    besr.p23_h_accum_init = ((tmp <<  4) & 0x000f8000) |
			    ((tmp << 12) & 0x70000000);
    tmp = (top & 0x0000ffff) + 0x00018000;
    besr.p1_v_accum_init = ((tmp << 4) & OV0_P1_V_ACCUM_INIT_MASK)
			    |(OV0_P1_MAX_LN_IN_PER_LN_OUT & 1);

    tmp = ((top >> 1) & 0x0000ffff) + 0x00018000;
    besr.p23_v_accum_init = (is_420||is_410) ?
			    ((tmp << 4) & OV0_P23_V_ACCUM_INIT_MASK)
			    |(OV0_P23_MAX_LN_IN_PER_LN_OUT & 1) : 0;

    leftUV = (left >> (is_410?18:17)) & 15;
    left = (left >> 16) & 15;
    if(is_rgb && !is_rgb32) h_inc<<=1;
    if(is_rgb32)
	besr.h_inc = (h_inc >> 1) | ((h_inc >> 1) << 16);
    else
    if(is_410)
	besr.h_inc = h_inc | ((h_inc >> 2) << 16);
    else
	besr.h_inc = h_inc | ((h_inc >> 1) << 16);
    besr.step_by = step_by | (step_by << 8);
    besr.y_x_start = (config->dest.x+X_ADJUST) | (config->dest.y << 16);
    besr.y_x_end = (config->dest.x + dest_w+X_ADJUST) | ((config->dest.y + dest_h) << 16);
    besr.p1_blank_lines_at_top = P1_BLNK_LN_AT_TOP_M1_MASK|((src_h-1)<<16);
    if(is_420 || is_410)
    {
	src_h = (src_h + 1) >> (is_410?2:1);
	besr.p23_blank_lines_at_top = P23_BLNK_LN_AT_TOP_M1_MASK|((src_h-1)<<16);
    }
    else besr.p23_blank_lines_at_top = 0;
    besr.vid_buf_pitch0_value = pitch;
    besr.vid_buf_pitch1_value = is_410 ? pitch>>2 : is_420 ? pitch>>1 : pitch;
    besr.p1_x_start_end = (src_w+left-1)|(left<<16);
    if (is_410||is_420) src_w>>=is_410?2:1;
    if(is_400)
    {
	besr.p2_x_start_end = 0;
	besr.p3_x_start_end = 0;
    }
    else
    {
	besr.p2_x_start_end = (src_w+left-1)|(leftUV<<16);
	besr.p3_x_start_end = besr.p2_x_start_end;
    }

    return 0;
}

static void radeon_compute_framesize(vidix_playback_t *info)
{
  unsigned pitch,awidth,dbpp;
  pitch = radeon_query_pitch(info->fourcc,&info->src.pitch);
  dbpp = radeon_vid_get_dbpp();
  switch(info->fourcc)
  {
    case IMGFMT_I420:
    case IMGFMT_YV12:
    case IMGFMT_IYUV:
		awidth = (info->src.w + (pitch-1)) & ~(pitch-1);
		info->frame_size = awidth*(info->src.h+info->src.h/2);
		break;
    case IMGFMT_Y800:
    case IMGFMT_Y8:
		awidth = (info->src.w + (pitch-1)) & ~(pitch-1);
		info->frame_size = awidth*info->src.h;
		break;
    case IMGFMT_IF09:
    case IMGFMT_YVU9:
		awidth = (info->src.w + (pitch-1)) & ~(pitch-1);
		info->frame_size = awidth*(info->src.h+info->src.h/8);
		break;
    case IMGFMT_RGB32:
    case IMGFMT_BGR32:
		awidth = (info->src.w*4 + (pitch-1)) & ~(pitch-1);
		info->frame_size = awidth*info->src.h;
		break;
    /* YUY2 YVYU, RGB15, RGB16 */
    default:	
		awidth = (info->src.w*2 + (pitch-1)) & ~(pitch-1);
		info->frame_size = awidth*info->src.h;
		break;
  }
}

int vixConfigPlayback(vidix_playback_t *info)
{
  unsigned rgb_size,nfr;
  if(!is_supported_fourcc(info->fourcc)) return ENOSYS;
  if(info->num_frames>VID_PLAY_MAXFRAMES) info->num_frames=VID_PLAY_MAXFRAMES;
  if(info->num_frames==1) besr.double_buff=0;
  else                    besr.double_buff=1;
  radeon_compute_framesize(info);
    
  rgb_size = radeon_get_xres()*radeon_get_yres()*((radeon_vid_get_dbpp()+7)/8);
  nfr = info->num_frames;
  for(;nfr>0; nfr--)
  {
      radeon_overlay_off = radeon_ram_size - info->frame_size*nfr;
#ifdef HAVE_X11
      radeon_overlay_off -= firegl_shift;
#endif
      radeon_overlay_off &= 0xffff0000;
      if(radeon_overlay_off >= (int)rgb_size ) break;
  }
  if(nfr <= 3)
  {
   nfr = info->num_frames;
   for(;nfr>0; nfr--)
   {
      radeon_overlay_off = radeon_ram_size - info->frame_size*nfr;
#ifdef HAVE_X11
      radeon_overlay_off -= firegl_shift;
#endif
      radeon_overlay_off &= 0xffff0000;
      if(radeon_overlay_off > 0) break;
   }
  }
  if(nfr <= 0) return EINVAL;
  info->num_frames = nfr;
  besr.vid_nbufs = info->num_frames;
  info->dga_addr = (char *)radeon_mem_base + radeon_overlay_off;  
  radeon_vid_init_video(info);
  return 0;
}

int vixPlaybackOn( void )
{
  radeon_vid_display_video();
  return 0;
}

int vixPlaybackOff( void )
{
  radeon_vid_stop_video();
  return 0;
}

int vixPlaybackFrameSelect(unsigned frame)
{
    uint32_t off[6];
    int prev_frame= (frame-1+besr.vid_nbufs) % besr.vid_nbufs;
    /*
    buf3-5 always should point onto second buffer for better
    deinterlacing and TV-in
    */
    if(!besr.double_buff) return 0;
    if(frame > besr.vid_nbufs) frame = besr.vid_nbufs-1;
    if(prev_frame > (int)besr.vid_nbufs) prev_frame = besr.vid_nbufs-1;
    off[0] = besr.vid_buf_base_adrs_y[frame];
    off[1] = besr.vid_buf_base_adrs_v[frame];
    off[2] = besr.vid_buf_base_adrs_u[frame];
    off[3] = besr.vid_buf_base_adrs_y[prev_frame];
    off[4] = besr.vid_buf_base_adrs_v[prev_frame];
    off[5] = besr.vid_buf_base_adrs_u[prev_frame];
    radeon_fifo_wait(8);
    OUTREG(OV0_REG_LOAD_CNTL,		REG_LD_CTL_LOCK);
    radeon_engine_idle();
    while(!(INREG(OV0_REG_LOAD_CNTL)&REG_LD_CTL_LOCK_READBACK));
    OUTREG(OV0_VID_BUF0_BASE_ADRS,	off[0]);
    OUTREG(OV0_VID_BUF1_BASE_ADRS,	off[1]);
    OUTREG(OV0_VID_BUF2_BASE_ADRS,	off[2]);
    OUTREG(OV0_VID_BUF3_BASE_ADRS,	off[3]);
    OUTREG(OV0_VID_BUF4_BASE_ADRS,	off[4]);
    OUTREG(OV0_VID_BUF5_BASE_ADRS,	off[5]);
    OUTREG(OV0_REG_LOAD_CNTL,		0);
    if(besr.vid_nbufs == 2) radeon_wait_vsync();
    if(__verbose > 1) radeon_vid_dump_regs();
    return 0;
}

vidix_video_eq_t equal =
{
 VEQ_CAP_BRIGHTNESS | VEQ_CAP_SATURATION
#ifndef RAGE128
 | VEQ_CAP_CONTRAST | VEQ_CAP_HUE | VEQ_CAP_RGB_INTENSITY
#endif
 ,
 0, 0, 0, 0, 0, 0, 0, 0 };

int 	vixPlaybackGetEq( vidix_video_eq_t * eq)
{
  memcpy(eq,&equal,sizeof(vidix_video_eq_t));
  return 0;
}

#ifndef RAGE128
#define RTFSaturation(a)   (1.0 + ((a)*1.0)/1000.0)
#define RTFBrightness(a)   (((a)*1.0)/2000.0)
#define RTFIntensity(a)    (((a)*1.0)/2000.0)
#define RTFContrast(a)   (1.0 + ((a)*1.0)/1000.0)
#define RTFHue(a)   (((a)*3.1416)/1000.0)
#define RTFCheckParam(a) {if((a)<-1000) (a)=-1000; if((a)>1000) (a)=1000;}
#endif

int 	vixPlaybackSetEq( const vidix_video_eq_t * eq)
{
#ifdef RAGE128
  int br,sat;
#else
  int itu_space;
#endif
    if(eq->cap & VEQ_CAP_BRIGHTNESS) equal.brightness = eq->brightness;
    if(eq->cap & VEQ_CAP_CONTRAST)   equal.contrast   = eq->contrast;
    if(eq->cap & VEQ_CAP_SATURATION) equal.saturation = eq->saturation;
    if(eq->cap & VEQ_CAP_HUE)        equal.hue        = eq->hue;
    if(eq->cap & VEQ_CAP_RGB_INTENSITY)
    {
      equal.red_intensity   = eq->red_intensity;
      equal.green_intensity = eq->green_intensity;
      equal.blue_intensity  = eq->blue_intensity;
    }
    equal.flags = eq->flags;
#ifdef RAGE128
    br = equal.brightness * 64 / 1000;
    if(br < -64) br = -64; if(br > 63) br = 63;
    sat = (equal.saturation + 1000) * 16 / 1000;
    if(sat < 0) sat = 0; if(sat > 31) sat = 31;
    OUTREG(OV0_COLOUR_CNTL, (br & 0x7f) | (sat << 8) | (sat << 16));
#else
  itu_space = equal.flags == VEQ_FLG_ITU_R_BT_709 ? 1 : 0;
  RTFCheckParam(equal.brightness);
  RTFCheckParam(equal.saturation);
  RTFCheckParam(equal.contrast);
  RTFCheckParam(equal.hue);
  RTFCheckParam(equal.red_intensity);
  RTFCheckParam(equal.green_intensity);
  RTFCheckParam(equal.blue_intensity);
  radeon_set_transform(RTFBrightness(equal.brightness),
		       RTFContrast(equal.contrast),
		       RTFSaturation(equal.saturation),
		       RTFHue(equal.hue),
		       RTFIntensity(equal.red_intensity),
		       RTFIntensity(equal.green_intensity),
		       RTFIntensity(equal.blue_intensity),
		       itu_space);
#endif
  return 0;
}

int 	vixPlaybackSetDeint( const vidix_deinterlace_t * info)
{
  unsigned sflg;
  switch(info->flags)
  {
    default:
    case CFG_NON_INTERLACED:
			    besr.deinterlace_on = 0;
			    break;
    case CFG_EVEN_ODD_INTERLACING:
    case CFG_INTERLACED:
			    besr.deinterlace_on = 1;
			    besr.deinterlace_pattern = 0x900AAAAA;
			    break;
    case CFG_ODD_EVEN_INTERLACING:
			    besr.deinterlace_on = 1;
			    besr.deinterlace_pattern = 0x00055555;
			    break;
    case CFG_UNIQUE_INTERLACING:
			    besr.deinterlace_on = 1;
			    besr.deinterlace_pattern = info->deinterlace_pattern;
			    break;
  }
  OUTREG(OV0_REG_LOAD_CNTL,		REG_LD_CTL_LOCK);
  radeon_engine_idle();
  while(!(INREG(OV0_REG_LOAD_CNTL)&REG_LD_CTL_LOCK_READBACK));
  radeon_fifo_wait(15);
  sflg = INREG(OV0_SCALE_CNTL);
  if(besr.deinterlace_on)
  {
    OUTREG(OV0_SCALE_CNTL,sflg | SCALER_ADAPTIVE_DEINT);
    OUTREG(OV0_DEINTERLACE_PATTERN,besr.deinterlace_pattern);
  }
  else OUTREG(OV0_SCALE_CNTL,sflg & (~SCALER_ADAPTIVE_DEINT));
  OUTREG(OV0_REG_LOAD_CNTL,		0);
  return 0;  
}

int 	vixPlaybackGetDeint( vidix_deinterlace_t * info)
{
  if(!besr.deinterlace_on) info->flags = CFG_NON_INTERLACED;
  else
  {
    info->flags = CFG_UNIQUE_INTERLACING;
    info->deinterlace_pattern = besr.deinterlace_pattern;
  }
  return 0;
}


/* Graphic keys */
static vidix_grkey_t radeon_grkey;

static void set_gr_key( void )
{
    if(radeon_grkey.ckey.op == CKEY_TRUE)
    {
	int dbpp=radeon_vid_get_dbpp();
	besr.ckey_on=1;

	switch(dbpp)
	{
	case 15:
#ifdef RADEON
		if(RadeonFamily > 100)
			besr.graphics_key_clr=
				  ((radeon_grkey.ckey.blue &0xF8))
				| ((radeon_grkey.ckey.green&0xF8)<<8)
				| ((radeon_grkey.ckey.red  &0xF8)<<16);
		else
#endif
		besr.graphics_key_clr=
			  ((radeon_grkey.ckey.blue &0xF8)>>3)
			| ((radeon_grkey.ckey.green&0xF8)<<2)
			| ((radeon_grkey.ckey.red  &0xF8)<<7);
		break;
	case 16:
#ifdef RADEON
		/* This test may be too general/specific */
		if(RadeonFamily > 100)
			besr.graphics_key_clr=
				  ((radeon_grkey.ckey.blue &0xF8))
				| ((radeon_grkey.ckey.green&0xFC)<<8)
				| ((radeon_grkey.ckey.red  &0xF8)<<16);
		else
#endif
		besr.graphics_key_clr=
			  ((radeon_grkey.ckey.blue &0xF8)>>3)
			| ((radeon_grkey.ckey.green&0xFC)<<3)
			| ((radeon_grkey.ckey.red  &0xF8)<<8);
		break;
	case 24:
		besr.graphics_key_clr=
			  ((radeon_grkey.ckey.blue &0xFF))
			| ((radeon_grkey.ckey.green&0xFF)<<8)
			| ((radeon_grkey.ckey.red  &0xFF)<<16);
		break;
	case 32:
		besr.graphics_key_clr=
			  ((radeon_grkey.ckey.blue &0xFF))
			| ((radeon_grkey.ckey.green&0xFF)<<8)
			| ((radeon_grkey.ckey.red  &0xFF)<<16);
		break;
	default:
		besr.ckey_on=0;
		besr.graphics_key_msk=0;
		besr.graphics_key_clr=0;
	}
#ifdef RAGE128
	besr.graphics_key_msk=(1<<dbpp)-1;
	besr.ckey_cntl = VIDEO_KEY_FN_TRUE|GRAPHIC_KEY_FN_NE|CMP_MIX_AND;
#else
	besr.graphics_key_msk=besr.graphics_key_clr;
	besr.ckey_cntl = VIDEO_KEY_FN_TRUE|CMP_MIX_AND|GRAPHIC_KEY_FN_EQ;
#endif
    }
    else
    {
	besr.ckey_on=0;
	besr.graphics_key_msk=0;
	besr.graphics_key_clr=0;
	besr.ckey_cntl = VIDEO_KEY_FN_TRUE|GRAPHIC_KEY_FN_TRUE|CMP_MIX_AND;
    }
    radeon_fifo_wait(3);
    OUTREG(OV0_GRAPHICS_KEY_MSK, besr.graphics_key_msk);
    OUTREG(OV0_GRAPHICS_KEY_CLR, besr.graphics_key_clr);
    OUTREG(OV0_KEY_CNTL,besr.ckey_cntl);
}

int vixGetGrKeys(vidix_grkey_t *grkey)
{
    memcpy(grkey, &radeon_grkey, sizeof(vidix_grkey_t));
    return(0);
}

int vixSetGrKeys(const vidix_grkey_t *grkey)
{
    memcpy(&radeon_grkey, grkey, sizeof(vidix_grkey_t));
    set_gr_key();
    return(0);
}
