view vidix/drivers/radeon_vid.c @ 9278:caea8ed36b48

The reason why mplayer crashes (in some cases) when using x11 output and -wid (>0) parameter is this: Mplayer by default creates a colormap using DirectColor visual. If the window given to mplayer uses TrueColor visual there will be an error when mplayer sets the colormap for the window. This patch modifies mplayer to use TrueColor visual if the window given to mplayer uses TrueColor. Another solution is to make sure that the window given to mplayer is created using DirectColor visual if it is supported by the display. Jouni Tulkki <jitulkki@cc.hut.fi>
author arpi
date Tue, 04 Feb 2003 18:31:44 +0000
parents 4898cfdf582a
children 97f61ffa441e
line wrap: on
line source

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

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;
#if defined(RAGE128) && (WORDS_BIGENDIAN)
static uint32_t SAVED_CONFIG_CNTL = 0;
#define APER_0_BIG_ENDIAN_16BPP_SWAP (1<<0)
#define APER_0_BIG_ENDIAN_32BPP_SWAP (2<<0)
#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,(uint32_t)(radeon_mmio_base),addr)
#define OUTREG8(addr,val)	SETREG(uint8_t,(uint32_t)(radeon_mmio_base),addr,val)

static inline uint32_t INREG (uint32_t addr) {
	uint32_t tmp = GETREG(uint32_t,(uint32_t)(radeon_mmio_base),addr);
	return le2me_32(tmp);
}
//#define OUTREG(addr,val)	SETREG(uint32_t,(uint32_t)(radeon_mmio_base),addr,val)
#define OUTREG(addr,val)	SETREG(uint32_t,(uint32_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;
}

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_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_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_RADEON_R300_ND,
 DEVICE_ATI_RADEON_R300_NE,
 DEVICE_ATI_RADEON_R300_NF,
 DEVICE_ATI_RADEON_R300_NG
#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}
};


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 occured 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);
#ifndef RAGE128	
	if(idx != -1)
	{
          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:
              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_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:
              RadeonFamily = 250;
              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:
              RadeonFamily = 300;
              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
  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:
      is_shift_required=1;
      break;
    default:
      break;
    }
#endif

/* XXX: hack, but it works for me (tm) */
#if defined(RAGE128) && (WORDS_BIGENDIAN)
    /* 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));
    }
#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");

#if defined(RAGE128) && (WORDS_BIGENDIAN)
    OUTREG(CONFIG_CNTL, SAVED_CONFIG_CNTL);
//    printf("saved: %x, restored: %x\n", SAVED_CONFIG_CNTL,
//	INREG(CONFIG_CNTL));
#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"*** 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;
    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;
    besr.v_inc = (src_h << 20) / dest_h;
    if(radeon_is_interlace()) besr.v_inc *= 2;
    h_inc = (src_w << 12) / dest_w;
    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
	    {
		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;
	}
	if(besr.fourcc == IMGFMT_I420 || besr.fourcc == IMGFMT_IYUV)
	{
	  uint32_t tmp;
	  tmp = config->offset.u;
	  config->offset.u = config->offset.v;
	  config->offset.v = tmp;
	}
    }
    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;
      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;
      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);
}