/*
 * Copyright (C) 2006 Claudio Ciccani <klan@users.sf.net>
 *
 * Graphics driver for ATI Radeon cards written by
 *             Claudio Ciccani <klan@users.sf.net>.  
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

#include <dfb_types.h>
#include <directfb.h>

#include <direct/types.h>
#include <direct/messages.h>
#include <direct/util.h>

#include <core/coredefs.h>
#include <core/coretypes.h>
#include <core/state.h>
#include <core/gfxcard.h>
#include <core/screens.h>
#include <core/surface.h>
#include <core/palette.h>
#include <core/system.h>

#include <fbdev/fb.h>

#include <gfx/convert.h>

#include <misc/conf.h>
#include <misc/util.h>

#include <core/graphics_driver.h>

DFB_GRAPHICS_DRIVER( radeon )


#include "radeon.h"
#include "radeon_chipsets.h"
#include "radeon_regs.h"
#include "radeon_mmio.h"
#include "radeon_state.h"
#include "radeon_2d.h"
#include "radeon_3d.h"


/* Enable the following option if you see strange behaviours */
#define RESET_AFTER_SETVAR  0


/* Driver capability flags */
#define RADEON_SUPPORTED_2D_DRAWINGFLAGS \
     ( DSDRAW_XOR )

#define RADEON_SUPPORTED_2D_DRAWINGFUNCS \
     ( DFXL_FILLRECTANGLE | DFXL_DRAWRECTANGLE | DFXL_DRAWLINE )

#define RADEON_SUPPORTED_2D_BLITTINGFLAGS \
     ( DSBLIT_XOR | DSBLIT_SRC_COLORKEY )

#define RADEON_SUPPORTED_2D_BLITTINGFUNCS \
     ( DFXL_BLIT )


#define R100_SUPPORTED_DRAWINGFLAGS \
     ( RADEON_SUPPORTED_2D_DRAWINGFLAGS | DSDRAW_BLEND | DSDRAW_SRC_PREMULTIPLY )

#define R100_SUPPORTED_DRAWINGFUNCS \
     ( RADEON_SUPPORTED_2D_DRAWINGFUNCS | DFXL_FILLTRIANGLE )

#define R100_SUPPORTED_BLITTINGFLAGS \
     ( RADEON_SUPPORTED_2D_BLITTINGFLAGS                   | \
       DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_BLEND_COLORALPHA | \
       DSBLIT_COLORIZE           | DSBLIT_SRC_PREMULTCOLOR | \
       DSBLIT_DEINTERLACE        | DSBLIT_ROTATE180        | \
       DSBLIT_SRC_MASK_ALPHA     | DSBLIT_SRC_MASK_COLOR   | \
       DSBLIT_SRC_PREMULTIPLY )

#define R100_SUPPORTED_BLITTINGFUNCS \
     ( RADEON_SUPPORTED_2D_BLITTINGFUNCS | DFXL_STRETCHBLIT | DFXL_TEXTRIANGLES )


#define R200_SUPPORTED_DRAWINGFLAGS \
     ( RADEON_SUPPORTED_2D_DRAWINGFLAGS | DSDRAW_BLEND | DSDRAW_SRC_PREMULTIPLY )

#define R200_SUPPORTED_DRAWINGFUNCS \
     ( RADEON_SUPPORTED_2D_DRAWINGFUNCS | DFXL_FILLTRIANGLE )

#define R200_SUPPORTED_BLITTINGFLAGS \
     ( RADEON_SUPPORTED_2D_BLITTINGFLAGS                   | \
       DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_BLEND_COLORALPHA | \
       DSBLIT_COLORIZE           | DSBLIT_SRC_PREMULTCOLOR | \
       DSBLIT_DEINTERLACE        | DSBLIT_ROTATE180        | \
       DSBLIT_SRC_MASK_ALPHA     | DSBLIT_SRC_MASK_COLOR   | \
       DSBLIT_SRC_PREMULTIPLY )

#define R200_SUPPORTED_BLITTINGFUNCS \
     ( RADEON_SUPPORTED_2D_BLITTINGFUNCS | DFXL_STRETCHBLIT | DFXL_TEXTRIANGLES )


#define R300_SUPPORTED_DRAWINGFLAGS \
     ( RADEON_SUPPORTED_2D_DRAWINGFLAGS | DSDRAW_BLEND | DSDRAW_SRC_PREMULTIPLY )

#define R300_SUPPORTED_DRAWINGFUNCS \
     ( RADEON_SUPPORTED_2D_DRAWINGFUNCS | DFXL_FILLTRIANGLE )

#define R300_SUPPORTED_BLITTINGFLAGS \
     ( RADEON_SUPPORTED_2D_BLITTINGFLAGS                   | \
       DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_BLEND_COLORALPHA | \
       DSBLIT_COLORIZE           | DSBLIT_SRC_PREMULTCOLOR | \
       DSBLIT_DEINTERLACE        | DSBLIT_ROTATE180 )
     
#define R300_SUPPORTED_BLITTINGFUNCS \
     ( RADEON_SUPPORTED_2D_BLITTINGFUNCS | DFXL_STRETCHBLIT | DFXL_TEXTRIANGLES )


#define DSBLIT_MODULATE_ALPHA ( DSBLIT_BLEND_ALPHACHANNEL | \
                                DSBLIT_BLEND_COLORALPHA )
#define DSBLIT_MODULATE_COLOR ( DSBLIT_BLEND_COLORALPHA   | \
                                DSBLIT_COLORIZE           | \
                                DSBLIT_SRC_PREMULTCOLOR )
#define DSBLIT_MODULATE       ( DSBLIT_MODULATE_ALPHA     | \
                                DSBLIT_MODULATE_COLOR     | \
                                DSBLIT_SRC_PREMULTIPLY )
#define DSBLIT_MASK           ( DSBLIT_SRC_MASK_ALPHA     | \
                                DSBLIT_SRC_MASK_COLOR )

#define RADEON_DRAW_3D()      ( rdev->accel & DFXL_FILLTRIANGLE  || \
                                rdev->drawingflags & ~DSDRAW_XOR || \
                                rdev->matrix != NULL             || \
                               (rdev->render_options & DSRO_ANTIALIAS && \
                                rdev->accel & DFXL_DRAWLINE) )

#define RADEON_BLIT_3D()      ( rdev->accel & ~DFXL_BLIT                     ||\
                                rdev->blittingflags & ~(DSBLIT_XOR |            \
                                                        DSBLIT_SRC_COLORKEY) || \
                                rdev->matrix != NULL                         || \
                               (rdev->dst_format != rdev->src_format        &&  \
                                !(DFB_PLANAR_PIXELFORMAT(rdev->dst_format) &&   \
                                  DFB_PLANAR_PIXELFORMAT(rdev->src_format)  )))
                                
#define RADEON_FUNC( f ) DFB_PLANAR_PIXELFORMAT(rdev->dst_format) ? f##_420 : f


static inline bool
radeon_compatible_format( RadeonDriverData *rdrv, DFBSurfacePixelFormat format )
{
#ifdef WORDS_BIGENDIAN
     u32 tmp, bpp;

     bpp = DFB_BYTES_PER_PIXEL( format );
     tmp = radeon_in32( rdrv->mmio_base, CRTC_GEN_CNTL );
     switch ((tmp >> 8) & 0xf) {
          case DST_8BPP:
          case DST_24BPP:
               if (bpp == 2 || bpp == 4)
                    return false;
               break;
          case DST_15BPP:
          case DST_16BPP:
               if (bpp != 2)
                    return false;
               break;
          default:
               if (bpp != 4)
                    return false;
               break;
     }
#endif
     return true;
}

static void
radeon_get_monitors( RadeonDriverData  *rdrv,
                     RadeonDeviceData  *rdev,
                     RadeonMonitorType *ret_monitor1,
                     RadeonMonitorType *ret_monitor2 )
{
     RadeonMonitorType  dvimon = MT_NONE;
     RadeonMonitorType  vgamon = MT_NONE;
#if D_DEBUG_ENABLED
     const char        *name[] = { "NONE", "CRT", "DFP", 
                                   "LCD",  "CTV", "STV" }; 
#endif
     u32                tmp;
      
     if (rdev->chipset != CHIP_R100) {        
          if (rdev->chipset >= CHIP_R300 ||
              rdev->chipset == CHIP_UNKNOWN)
               tmp = radeon_in32( rdrv->mmio_base, BIOS_0_SCRATCH );
          else
               tmp = radeon_in32( rdrv->mmio_base, BIOS_4_SCRATCH );
     
          /* DVI/TVO port */
		if (tmp & 0x08)
			dvimon = MT_DFP;
		else if (tmp & 0x4)
			dvimon = MT_LCD;
		else if (tmp & 0x200)
			dvimon = MT_CRT;
		else if (tmp & 0x10)
			dvimon = MT_CTV;
		else if (tmp & 0x20)
			dvimon = MT_STV;
		
          /* VGA port */
		if (tmp & 0x2)
			vgamon = MT_CRT;
		else if (tmp & 0x800)
			vgamon = MT_DFP;
		else if (tmp & 0x400)
			vgamon = MT_LCD;
		else if (tmp & 0x1000)
			vgamon = MT_CTV;
		else if (tmp & 0x2000)
			vgamon = MT_STV;
     }
     else {
          tmp = radeon_in32( rdrv->mmio_base, FP_GEN_CNTL );
          
          if (tmp & FP_EN_TMDS)
               vgamon = MT_DFP;
          else
               vgamon = MT_CRT;
     }

     D_DEBUG( "DirectFB/Radeon: "
              "DVI/TVO Port -> %s, VGA Port -> %s.\n", 
              name[dvimon], name[vgamon] );
     
     if (dvimon) {
          /* If DVI port is connected, then
           * DVI port is the primary head and 
           * CRT port is the secondary head.
           */
          if (ret_monitor1)
               *ret_monitor1 = dvimon;
          if (ret_monitor2)
               *ret_monitor2 = vgamon;
     } 
     else {
          if (ret_monitor1)
               *ret_monitor1 = vgamon;
          if (ret_monitor2)
               *ret_monitor2 = MT_NONE;
     }     
}

void
radeon_reset( RadeonDriverData *rdrv, RadeonDeviceData *rdev )
{
     volatile u8   *mmio = rdrv->mmio_base;
     u32            clock_cntl_index;
     u32            mclk_cntl;
     u32            rbbm_soft_reset;
     u32            host_path_cntl;
     
     clock_cntl_index = radeon_in32( mmio, CLOCK_CNTL_INDEX );
     
     mclk_cntl  = radeon_inpll( mmio, MCLK_CNTL );
     radeon_outpll( mmio, MCLK_CNTL, mclk_cntl     |
                                     FORCEON_MCLKA |
                                     FORCEON_MCLKB |
                                     FORCEON_YCLKA |
                                     FORCEON_YCLKB |
                                     FORCEON_MC    |
                                     FORCEON_AIC );

     host_path_cntl  = radeon_in32( mmio, HOST_PATH_CNTL );
     rbbm_soft_reset = radeon_in32( mmio, RBBM_SOFT_RESET );
   
     radeon_out32( mmio, 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 );
     radeon_in32( mmio, RBBM_SOFT_RESET );
     
     radeon_out32( mmio, 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) );
     radeon_in32( mmio, RBBM_SOFT_RESET );
     
     radeon_out32( mmio, HOST_PATH_CNTL, host_path_cntl | HDP_SOFT_RESET );
     radeon_in32( mmio, HOST_PATH_CNTL );
     radeon_out32( mmio, HOST_PATH_CNTL, host_path_cntl );

     radeon_out32( mmio, RBBM_SOFT_RESET, rbbm_soft_reset );

     radeon_out32( mmio, CLOCK_CNTL_INDEX, clock_cntl_index );
     radeon_outpll( mmio, MCLK_CNTL, mclk_cntl );
     
     rdev->set = 0;
     rdev->src_format = DSPF_UNKNOWN;
     rdev->dst_format = DSPF_UNKNOWN;
     rdev->fifo_space = 0;
}
     
static void radeonAfterSetVar( void *drv, void *dev )
{
     RadeonDriverData *rdrv = (RadeonDriverData*) drv;
     RadeonDeviceData *rdev = (RadeonDeviceData*) dev;
     volatile u8      *mmio = rdrv->mmio_base;
    
     rdev->surface_cntl   =
     rdev->surface_cntl_c =
     rdev->surface_cntl_p = radeon_in32( mmio, SURFACE_CNTL );

     radeon_out32( mmio, CRTC_OFFSET_CNTL, 
                  (radeon_in32( mmio, CRTC_OFFSET_CNTL ) & ~CRTC_TILE_EN) | CRTC_HSYNC_EN );

#if RESET_AFTER_SETVAR
     radeon_waitidle( rdrv, rdev );
     radeon_reset( rdrv, rdev );
#endif
}

static void radeonEngineReset( void *drv, void *dev )
{
     RadeonDriverData *rdrv = (RadeonDriverData*) drv;
     RadeonDeviceData *rdev = (RadeonDeviceData*) dev;
     volatile u8      *mmio = rdrv->mmio_base;

     rdev->fifo_space = 0;

     radeon_out32( mmio, SURFACE_CNTL, rdev->surface_cntl_c );
     
     radeon_waitfifo( rdrv, rdev, 3 );
#ifdef WORDS_BIGENDIAN
     radeon_out32( mmio, DP_DATATYPE, 
                   radeon_in32( mmio, DP_DATATYPE ) | HOST_BIG_ENDIAN_EN );
#else
     radeon_out32( mmio, DP_DATATYPE, 
                   radeon_in32( mmio, DP_DATATYPE ) & ~HOST_BIG_ENDIAN_EN );
#endif
     radeon_out32( mmio, DEFAULT_SC_BOTTOM_RIGHT, DEFAULT_SC_RIGHT_MAX |
                                                  DEFAULT_SC_BOTTOM_MAX );
     radeon_out32( mmio, AUX_SC_CNTL, 0 );
    
     if (rdev->chipset >= CHIP_R300) {
          r300_restore( rdrv, rdev );
     }
     else if (rdev->chipset >= CHIP_R200) {
          r200_restore( rdrv, rdev );
     }
     else if (rdev->chipset >= CHIP_R100) {
          r100_restore( rdrv, rdev );
     }
     
     /* sync 2d and 3d engines */
     radeon_waitfifo( rdrv, rdev, 1 );
     radeon_out32( mmio, ISYNC_CNTL, ISYNC_ANY2D_IDLE3D |
                                     ISYNC_ANY3D_IDLE2D ); 
}

static DFBResult radeonEngineSync( void *drv, void *dev )
{
     if (!radeon_waitidle( (RadeonDriverData*)drv, (RadeonDeviceData*)dev ))
     	return DFB_IO; /* DFB_TIMEOUT !? */

     return DFB_OK;
}

static void radeonInvalidateState( void *drv, void *dev )
{
     RadeonDeviceData *rdev = (RadeonDeviceData*) dev;
 
     rdev->set = 0;
     rdev->dst_format = DSPF_UNKNOWN;
     rdev->src_format = DSPF_UNKNOWN;
     rdev->msk_format = DSPF_UNKNOWN;
}

static void radeonFlushTextureCache( void *drv, void *dev )
{
     RadeonDriverData *rdrv = (RadeonDriverData*) drv;
     RadeonDeviceData *rdev = (RadeonDeviceData*) dev;
     volatile u8      *mmio = rdrv->mmio_base;
     
     if (rdev->chipset >= CHIP_R300) {
          if (rdrv->mmio_size > 0x4000) {     
               radeon_waitfifo( rdrv, rdev, 1 );
               radeon_out32( mmio, R300_TX_CNTL, 0 );
          }
     }
     else if (rdev->chipset >= CHIP_R200) {
          radeon_waitfifo( rdrv, rdev, 2 );
          radeon_out32( mmio, R200_PP_TXOFFSET_0, rdev->src_offset );
          radeon_out32( mmio, R200_PP_TXOFFSET_1, rdev->msk_offset );
     }
     else if (rdev->chipset >= CHIP_R100) {     
          radeon_waitfifo( rdrv, rdev, 2 );
          radeon_out32( mmio, PP_TXOFFSET_0, rdev->src_offset );
          radeon_out32( mmio, PP_TXOFFSET_1, rdev->msk_offset );
     }
}

#ifdef WORDS_BIGENDIAN
static void radeonSurfaceEnter( void *drv, void *dev, 
                                CoreSurfaceBuffer *buffer, DFBSurfaceLockFlags flags )
{
     RadeonDriverData *rdrv = (RadeonDriverData*) drv;
     RadeonDeviceData *rdev = (RadeonDeviceData*) dev;
     volatile u8      *mmio = rdrv->mmio_base;
     u32               tmp;

     if (!(flags & DSLF_WRITE))
          return;

     rdev->surface_cntl_p = radeon_in32( mmio, SURFACE_CNTL );
     
     tmp = rdev->surface_cntl_p & ~SURF_TRANSLATION_DIS;
     tmp &= ~(NONSURF_AP0_SWP_16BPP | NONSURF_AP1_SWP_16BPP |
              NONSURF_AP0_SWP_32BPP | NONSURF_AP1_SWP_32BPP);
              
     switch (DFB_BITS_PER_PIXEL( buffer->format )) {
          case 16:
               tmp |= NONSURF_AP0_SWP_16BPP | NONSURF_AP1_SWP_16BPP;
               break;
          case 32:
               tmp |= NONSURF_AP0_SWP_32BPP | NONSURF_AP1_SWP_32BPP;
               break;
          default:
               break;
     }

     radeon_out32( mmio, SURFACE_CNTL, tmp );
     rdev->surface_cntl_c = tmp;
}

static void radeonSurfaceLeave( void *drv, void *dev, CoreSurfaceBuffer *buffer )
{ 
     RadeonDriverData *rdrv = (RadeonDriverData*) drv;
     RadeonDeviceData *rdev = (RadeonDeviceData*) dev;
     volatile u8      *mmio = rdrv->mmio_base;
     
     if (rdev->surface_cntl_p != rdev->surface_cntl_c) {
          radeon_out32( mmio, SURFACE_CNTL, rdev->surface_cntl_p );
          rdev->surface_cntl_c = rdev->surface_cntl_p;
     }
}
#endif
          
static void r100CheckState( void *drv, void *dev,
                            CardState *state, DFBAccelerationMask accel )
{
     RadeonDeviceData *rdev        = (RadeonDeviceData*) dev;
     CoreSurface      *destination = state->destination;
     CoreSurface      *source      = state->source;
     
     int supported_drawingfuncs  = R100_SUPPORTED_DRAWINGFUNCS; 
     int supported_drawingflags  = R100_SUPPORTED_DRAWINGFLAGS;
     int supported_blittingfuncs = R100_SUPPORTED_BLITTINGFUNCS;
     int supported_blittingflags = R100_SUPPORTED_BLITTINGFLAGS;
   
     if (!radeon_compatible_format( drv, destination->config.format ))
          return;

     switch (destination->config.format) {               
          case DSPF_A8:
               if (state->src_blend == DSBF_SRCALPHASAT) {
                    supported_drawingflags  &= ~DSDRAW_BLEND;
                    supported_blittingflags &= ~DSBLIT_MODULATE_ALPHA;
               }
               break;
               
          case DSPF_RGB332:
          case DSPF_RGB444:
          case DSPF_ARGB4444:
          case DSPF_RGB555:
          case DSPF_ARGB1555:
          case DSPF_RGB16:
          case DSPF_RGB32:
          case DSPF_ARGB:
               break;
               
          case DSPF_LUT8:
          case DSPF_ALUT44:
               if (DFB_BLITTING_FUNCTION( accel ) &&
                   source->config.format != destination->config.format)
                    return;
               supported_drawingflags   =  DSDRAW_NOFX;
               supported_blittingfuncs &= ~DFXL_TEXTRIANGLES;
               supported_blittingflags  = ~(DSBLIT_MODULATE | DSBLIT_MASK);
               break;

          case DSPF_ARGB2554:
               if (DFB_BLITTING_FUNCTION( accel ) &&
                   source->config.format != destination->config.format)
                    return;
               supported_drawingfuncs  &= ~DFXL_FILLTRIANGLE;
               supported_drawingflags   =  DSDRAW_XOR;
               supported_blittingfuncs &= ~DFXL_TEXTRIANGLES;
               supported_blittingflags &= ~(DSBLIT_MODULATE | DSBLIT_MASK);
               break;
               
          case DSPF_AiRGB:
               supported_drawingflags  &= ~DSDRAW_BLEND;
               supported_blittingflags &= ~DSBLIT_MODULATE_ALPHA;
               break;

          case DSPF_I420:
          case DSPF_YV12:
               if (DFB_BLITTING_FUNCTION( accel )     &&
                   source->config.format != DSPF_A8   &&
                   source->config.format != DSPF_I420 &&
                   source->config.format != DSPF_YV12)
                    return;
          case DSPF_YUY2:
          case DSPF_UYVY:
               if (source && source->config.format != DSPF_A8)
                   supported_blittingflags &= ~(DSBLIT_COLORIZE | DSBLIT_SRC_COLORKEY | DSBLIT_MASK);
               break;

          case DSPF_AYUV:
               if (DFB_BLITTING_FUNCTION( accel ) && source->config.format != DSPF_A8) {
                    if (source->config.format != DSPF_AYUV)
                         return;
                    supported_blittingflags &= ~(DSBLIT_COLORIZE | DSBLIT_MASK);
               }
               break;
               
          default:
               return;
     }

     if (DFB_BLITTING_FUNCTION( accel )) { 
          if (state->blittingflags & DSBLIT_MASK ||
              state->blittingflags & DSBLIT_SRC_PREMULTIPLY) {
               if (state->blittingflags & DSBLIT_MASK &&
                   state->blittingflags & DSBLIT_SRC_PREMULTIPLY)
                    return;
               if (state->blittingflags & (DSBLIT_COLORIZE | DSBLIT_SRC_PREMULTCOLOR))
                    return;
          }

          if (state->blittingflags & DSBLIT_SRC_COLORKEY) {
               if (destination->config.format != source->config.format)
                    return;
               supported_blittingfuncs  = DFXL_BLIT;
               supported_blittingflags &= DSBLIT_SRC_COLORKEY | DSBLIT_XOR;
          }
          
          if (state->blittingflags & DSBLIT_ROTATE180)
               supported_blittingfuncs &= ~DFXL_TEXTRIANGLES;
               
          if (accel & ~supported_blittingfuncs ||
              state->blittingflags & ~supported_blittingflags)
               return;
          
          if (source->config.size.w > 2048 || source->config.size.h > 2048)
               return;

          if (state->blittingflags & DSBLIT_MODULATE_ALPHA &&
              state->dst_blend == DSBF_SRCALPHASAT)
               return;
               
          if (!radeon_compatible_format( drv, source->config.format ))
               return;

          switch (source->config.format) {           
               case DSPF_RGB332:
               case DSPF_RGB444:
               case DSPF_ARGB4444:
               case DSPF_RGB555:
               case DSPF_ARGB1555:
               case DSPF_RGB16:
               case DSPF_RGB32:
               case DSPF_ARGB:
                    if (destination->config.format == DSPF_UYVY ||
                        destination->config.format == DSPF_YUY2)
                         return;
               case DSPF_A8:
               case DSPF_YUY2:
               case DSPF_UYVY:
                    break;

               case DSPF_AiRGB:
                    if (destination->config.format != source->config.format &&
                        (DFB_PIXELFORMAT_HAS_ALPHA(destination->config.format) ||
                         destination->config.format == DSPF_UYVY ||
                         destination->config.format == DSPF_YUY2))
                         return;
                    if (state->blittingflags & DSBLIT_SRC_PREMULTIPLY)
                         return;
                    break;
               
               case DSPF_LUT8:
               case DSPF_ALUT44:
               case DSPF_ARGB2554:
               case DSPF_AYUV:
                    if (destination->config.format != source->config.format)
                         return;
                    break;

               case DSPF_I420:
               case DSPF_YV12:
                    if (source->config.size.w < 2 || source->config.size.h < 2)
                         return;
                    if (destination->config.format != DSPF_I420 &&
                        destination->config.format != DSPF_YV12)
                         return;
                    break;
               
               default:
                    return;
          }

          if (state->blittingflags & DSBLIT_MASK) {
               CoreSurface *mask = state->source_mask;
               
               if (state->blittingflags & (DSBLIT_BLEND_COLORALPHA |
                                           DSBLIT_COLORIZE | DSBLIT_SRC_PREMULTCOLOR))
                    return;
               
               if (state->src_mask_flags & DSMF_STENCIL ||
                   state->src_mask_offset.x || state->src_mask_offset.y)
                    return;
                    
               if (mask->config.size.w > 2048 || mask->config.size.h > 2048)
                    return;
                    
               if (!radeon_compatible_format( drv, mask->config.format ))
                    return;
                    
               switch (mask->config.format) {
                    case DSPF_A8:
                    case DSPF_RGB332:
                    case DSPF_RGB444:
                    case DSPF_ARGB4444:
                    case DSPF_RGB555:
                    case DSPF_ARGB1555:
                    case DSPF_RGB16:
                    case DSPF_RGB32:
                    case DSPF_ARGB:
                         break;

                    case DSPF_AiRGB:
                         if (state->blittingflags & DSBLIT_SRC_MASK_ALPHA &&
                             DFB_PIXELFORMAT_HAS_ALPHA(source->config.format) &&
                             source->config.format != DSPF_AiRGB)
                              return;
                         break;
                    
                    default:
                         return; 
               }
          }

          state->accel |= supported_blittingfuncs;
          rdev->blitting_mask = supported_blittingfuncs;
     } 
     else {
          if (accel & ~supported_drawingfuncs ||
              state->drawingflags & ~supported_drawingflags)
               return;

          if (state->drawingflags & DSDRAW_BLEND &&
              state->dst_blend == DSBF_SRCALPHASAT)
               return;
               
          state->accel |= supported_drawingfuncs;
          rdev->drawing_mask = supported_drawingfuncs;
     }
}

static void r200CheckState( void *drv, void *dev,
                            CardState *state, DFBAccelerationMask accel )
{
     RadeonDeviceData *rdev        = (RadeonDeviceData*) dev;
     CoreSurface      *destination = state->destination;
     CoreSurface      *source      = state->source;
     
     int supported_drawingfuncs  = R200_SUPPORTED_DRAWINGFUNCS; 
     int supported_drawingflags  = R200_SUPPORTED_DRAWINGFLAGS;
     int supported_blittingfuncs = R200_SUPPORTED_BLITTINGFUNCS;
     int supported_blittingflags = R200_SUPPORTED_BLITTINGFLAGS;
     
     if (!radeon_compatible_format( drv, destination->config.format ))
          return;
     
     switch (destination->config.format) {               
          case DSPF_A8:
               if (state->src_blend == DSBF_SRCALPHASAT) {
                    supported_drawingflags  &= ~DSDRAW_BLEND;
                    supported_blittingflags &= ~DSBLIT_MODULATE_ALPHA;
               }
               break;
                   
          case DSPF_RGB332:
          case DSPF_RGB444:
          case DSPF_ARGB4444:
          case DSPF_RGB555:
          case DSPF_ARGB1555:
          case DSPF_RGB16:
          case DSPF_RGB32:
          case DSPF_ARGB:
               break;
               
          case DSPF_LUT8:
          case DSPF_ALUT44:
               if (DFB_BLITTING_FUNCTION( accel ) &&
                   source->config.format != destination->config.format)
                    return;
               supported_drawingflags   =  DSDRAW_NOFX;
               supported_blittingfuncs &= ~DFXL_TEXTRIANGLES;
               supported_blittingflags &= ~(DSBLIT_MODULATE | DSBLIT_MASK);
               break;

          case DSPF_ARGB2554:
               if (DFB_BLITTING_FUNCTION( accel ) &&
                   source->config.format != destination->config.format)
                    return;
               supported_drawingfuncs  &= ~DFXL_FILLTRIANGLE;
               supported_drawingflags   =  DSDRAW_XOR;
               supported_blittingfuncs &= ~DFXL_TEXTRIANGLES;
               supported_blittingflags &= ~(DSBLIT_MODULATE | DSBLIT_MASK);
               break;
               
          case DSPF_AiRGB:
               supported_drawingflags  &= ~DSDRAW_BLEND;
               supported_blittingflags &= ~DSBLIT_MODULATE_ALPHA;
               break;

          case DSPF_I420:
          case DSPF_YV12:
               if (DFB_BLITTING_FUNCTION( accel ) &&
                   source->config.format != DSPF_A8      &&
                   source->config.format != DSPF_I420    &&
                   source->config.format != DSPF_YV12)
                    return;
          case DSPF_YUY2:
          case DSPF_UYVY:
               if (source && source->config.format != DSPF_A8)
                    supported_blittingflags &= ~(DSBLIT_COLORIZE | DSBLIT_SRC_COLORKEY | DSBLIT_MASK);
               break;
               
          case DSPF_AYUV:
               if (DFB_BLITTING_FUNCTION( accel ) && source->config.format != DSPF_A8) {
                    if (source->config.format != DSPF_AYUV)
                         return;
                    supported_blittingflags &= ~(DSBLIT_COLORIZE | DSBLIT_MASK);
               }
               break;
          
          default:
               return;
     }

     if (DFB_BLITTING_FUNCTION( accel )) {
          if (state->blittingflags & DSBLIT_MASK ||
              state->blittingflags & DSBLIT_SRC_PREMULTIPLY) {
               if (state->blittingflags & DSBLIT_MASK &&
                   state->blittingflags & DSBLIT_SRC_PREMULTIPLY)
                    return;
               if (state->blittingflags & (DSBLIT_COLORIZE | DSBLIT_SRC_PREMULTCOLOR))
                    return;
          }
              
          if (state->blittingflags & DSBLIT_SRC_COLORKEY) {
               if (destination->config.format != source->config.format)
                    return;
               supported_blittingfuncs  = DFXL_BLIT;
               supported_blittingflags &= DSBLIT_SRC_COLORKEY | DSBLIT_XOR;
          }
          
          if (state->blittingflags & DSBLIT_ROTATE180)
               supported_blittingfuncs &= ~DFXL_TEXTRIANGLES;
               
          if (accel & ~supported_blittingfuncs ||
              state->blittingflags & ~supported_blittingflags)
               return;
          
          if (source->config.size.w > 2048 || source->config.size.h > 2048)
               return;

          if (state->blittingflags & DSBLIT_MODULATE_ALPHA &&
              state->dst_blend == DSBF_SRCALPHASAT)
               return;
         
          if (!radeon_compatible_format( drv, source->config.format ))
               return;
         
          switch (source->config.format) {                    
               case DSPF_RGB332:
               case DSPF_RGB444:
               case DSPF_ARGB4444:
               case DSPF_RGB555:
               case DSPF_ARGB1555:
               case DSPF_RGB16:
               case DSPF_RGB32:
               case DSPF_ARGB:
                    if (destination->config.format == DSPF_UYVY ||
                        destination->config.format == DSPF_YUY2)
                         return;
               case DSPF_A8:
                    break;

               case DSPF_AiRGB:
                    if (destination->config.format != source->config.format &&
                        (DFB_PIXELFORMAT_HAS_ALPHA(destination->config.format) ||
                         destination->config.format == DSPF_UYVY ||
                         destination->config.format == DSPF_YUY2))
                         return;
                    if (state->blittingflags & DSBLIT_SRC_PREMULTIPLY)
                         return;
                    break;
               
               case DSPF_LUT8:
               case DSPF_ALUT44:
               case DSPF_ARGB2554:
               case DSPF_AYUV:
                    if (destination->config.format != source->config.format)
                         return;
                    break;
                
               case DSPF_YUY2:
               case DSPF_UYVY:
                    if (rdev->chipset == CHIP_RV250             &&
                        destination->config.format != DSPF_YUY2 &&
                        destination->config.format != DSPF_UYVY)
                         return;
                    break;

               case DSPF_I420:
               case DSPF_YV12:
                    if (source->config.size.w < 2 || source->config.size.h < 2)
                         return;
                    if (destination->config.format != DSPF_I420 &&
                        destination->config.format != DSPF_YV12)
                         return;
                    break;
               
               default:
                    return;
          }
          
          if (state->blittingflags & DSBLIT_MASK) {
               CoreSurface *mask = state->source_mask;
               
               if (state->blittingflags & (DSBLIT_BLEND_COLORALPHA |
                                           DSBLIT_COLORIZE | DSBLIT_SRC_PREMULTCOLOR))
                    return;
               
               if (state->src_mask_flags & DSMF_STENCIL ||
                   state->src_mask_offset.x || state->src_mask_offset.y)
                    return;
                    
               if (mask->config.size.w > 2048 || mask->config.size.h > 2048)
                    return;
                    
               if (!radeon_compatible_format( drv, mask->config.format ))
                    return;
                    
               switch (mask->config.format) {
                    case DSPF_A8:
                    case DSPF_RGB332:
                    case DSPF_RGB444:
                    case DSPF_ARGB4444:
                    case DSPF_RGB555:
                    case DSPF_ARGB1555:
                    case DSPF_RGB16:
                    case DSPF_RGB32:
                    case DSPF_ARGB:
                         break;
               
                    case DSPF_AiRGB:
                         if (state->blittingflags & DSBLIT_SRC_MASK_ALPHA &&
                             DFB_PIXELFORMAT_HAS_ALPHA(source->config.format) &&
                             source->config.format != DSPF_AiRGB)
                              return;
                         break;

                    default:
                         return; 
               }
          }

          state->accel |= supported_blittingfuncs;
          rdev->blitting_mask = supported_blittingfuncs;
     } 
     else {
          if (accel & ~supported_drawingfuncs ||
              state->drawingflags & ~supported_drawingflags)
               return;

          if (state->drawingflags & DSDRAW_BLEND &&
              state->dst_blend == DSBF_SRCALPHASAT)
               return;
               
          state->accel |= supported_drawingfuncs;
          rdev->drawing_mask = supported_drawingfuncs;
     }
}

static void r300CheckState( void *drv, void *dev,
                            CardState *state, DFBAccelerationMask accel )
{
     RadeonDriverData *rdrv        = (RadeonDriverData*) drv;
     RadeonDeviceData *rdev        = (RadeonDeviceData*) dev;
     CoreSurface      *destination = state->destination;
     CoreSurface      *source      = state->source;
     bool              can_convert = true;
    
     int supported_drawingfuncs  = R300_SUPPORTED_DRAWINGFUNCS; 
     int supported_drawingflags  = R300_SUPPORTED_DRAWINGFLAGS;
     int supported_blittingfuncs = R300_SUPPORTED_BLITTINGFUNCS;
     int supported_blittingflags = R300_SUPPORTED_BLITTINGFLAGS;
     if (rdrv->mmio_size <= 0x4000) {
          supported_drawingfuncs = RADEON_SUPPORTED_2D_DRAWINGFUNCS;
          supported_drawingflags = RADEON_SUPPORTED_2D_DRAWINGFLAGS;
          supported_blittingfuncs = RADEON_SUPPORTED_2D_BLITTINGFUNCS;
          supported_blittingflags = RADEON_SUPPORTED_2D_BLITTINGFLAGS;
          can_convert = false;
     }

     if (!radeon_compatible_format( drv, destination->config.format ))
          return;
     
     switch (destination->config.format) {
          case DSPF_RGB16:
          case DSPF_RGB32:
          case DSPF_ARGB:
               break;
               
          case DSPF_LUT8:
          case DSPF_ALUT44:
          case DSPF_A8:
          case DSPF_RGB332:
          case DSPF_RGB444:
          case DSPF_ARGB4444:
          case DSPF_ARGB2554:
          case DSPF_RGB555:
          case DSPF_ARGB1555:
          case DSPF_AiRGB:
          case DSPF_AYUV:
          case DSPF_YUY2:
          case DSPF_UYVY:
               if (DFB_BLITTING_FUNCTION( accel )) {
                    if (source->config.format != destination->config.format)
                         return;
               }
               supported_drawingfuncs  &= ~DFXL_FILLTRIANGLE;
               supported_drawingflags   =  DSDRAW_NOFX; 
               supported_blittingfuncs  =  DFXL_BLIT;
               supported_blittingflags  =  DSBLIT_NOFX;
               break;
 
          case DSPF_I420:
          case DSPF_YV12:
               if (DFB_BLITTING_FUNCTION( accel )) {
                    if (source->config.format != DSPF_I420 &&
                        source->config.format != DSPF_YV12)
                         return;
               }
               supported_drawingfuncs  &= ~DFXL_FILLTRIANGLE;
               supported_drawingflags   =  DSDRAW_XOR;
               supported_blittingflags  =  DSBLIT_DEINTERLACE;
               break;
               
          default:
               return;
     }

     if (DFB_BLITTING_FUNCTION( accel )) {
          if (state->blittingflags & DSBLIT_XOR) {
			can_convert = false;
               supported_blittingfuncs  = DFXL_BLIT;
               supported_blittingflags &= DSBLIT_SRC_COLORKEY | DSBLIT_XOR;
          }
          
          if (state->blittingflags & DSBLIT_BLEND_ALPHACHANNEL) {
               if (state->blittingflags & DSBLIT_BLEND_COLORALPHA)
                    return;
               if (state->blittingflags & (DSBLIT_COLORIZE | DSBLIT_SRC_PREMULTCOLOR)) {
                    if (state->src_blend != DSBF_ONE)
                         return;
               }
          }
          
          if (state->blittingflags & DSBLIT_ROTATE180)
               supported_blittingfuncs &= ~DFXL_TEXTRIANGLES;
          
          if (accel & ~supported_blittingfuncs ||
              state->blittingflags & ~supported_blittingflags)
               return;
          
          if (source->config.size.w > 2048 || source->config.size.h > 2048)
               return;

          if (state->blittingflags & DSBLIT_MODULATE_ALPHA &&
              state->dst_blend == DSBF_SRCALPHASAT)
               return;
               
          if (!radeon_compatible_format( drv, source->config.format ))
               return;
               
          switch (source->config.format) {
               case DSPF_A8:                   
               case DSPF_RGB332:
               case DSPF_RGB444:
               case DSPF_ARGB4444:
               case DSPF_RGB555:
               case DSPF_ARGB1555:
               case DSPF_RGB16:
               case DSPF_RGB32:
               case DSPF_ARGB:
                    if (!can_convert &&
                        destination->config.format != source->config.format)
                         return;
                    break;
               
               case DSPF_LUT8:
               case DSPF_ALUT44:
                    if (destination->config.format != source->config.format)
                         return;
                    break;

               case DSPF_ARGB2554:
               case DSPF_AiRGB:
               case DSPF_AYUV:
               case DSPF_YUY2:
               case DSPF_UYVY:
                    if (destination->config.format != source->config.format)
                         return;
                    break;
                    
               case DSPF_I420:
               case DSPF_YV12:
                    if (source->config.size.w < 2 || source->config.size.h < 2)
                         return;
                    if (destination->config.format != DSPF_I420 &&
                        destination->config.format != DSPF_YV12)
                         return;
                    break;
               
               default:
                    return;
          }
          
          state->accel |= supported_blittingfuncs;
          rdev->blitting_mask = supported_blittingfuncs;
     } 
     else {
          if (state->drawingflags & DSDRAW_XOR) {
               supported_drawingfuncs &= ~DFXL_FILLTRIANGLE;
               supported_drawingflags &= DSDRAW_XOR;
          }
          
          if (accel & ~supported_drawingfuncs ||
              state->drawingflags & ~supported_drawingflags)
               return;

          if (state->drawingflags & DSDRAW_BLEND &&
              state->dst_blend == DSBF_SRCALPHASAT)
               return;
               
          state->accel |= supported_drawingfuncs;
          rdev->drawing_mask = supported_drawingfuncs;
     }
}

static void r100SetState( void *drv, void *dev,
                          GraphicsDeviceFuncs *funcs,
                          CardState *state, DFBAccelerationMask accel )
{
     RadeonDriverData *rdrv = (RadeonDriverData*) drv;
     RadeonDeviceData *rdev = (RadeonDeviceData*) dev;
 
     rdev->set &= ~state->mod_hw;
     if (DFB_BLITTING_FUNCTION( accel )) {
          if ((rdev->accel ^ accel) & DFXL_TEXTRIANGLES)
               rdev->set &= ~SMF_BLITTING_FLAGS;
     }
     
     rdev->accel = accel;
     
     r100_set_destination( rdrv, rdev, state );
     r100_set_clip( rdrv, rdev, state );
     r100_set_render_options( rdrv, rdev, state );
    
     switch (accel) {
          case DFXL_FILLRECTANGLE:
          case DFXL_FILLTRIANGLE:
          case DFXL_DRAWRECTANGLE:
          case DFXL_DRAWLINE:
               r100_set_drawing_color( rdrv, rdev, state );
               
               if (state->drawingflags & DSDRAW_BLEND)
                    r100_set_blend_function( rdrv, rdev, state );
               
               r100_set_drawingflags( rdrv, rdev, state );

               if (RADEON_DRAW_3D()) {
                    funcs->FillRectangle = r100FillRectangle3D;
                    funcs->FillTriangle  = r100FillTriangle;
                    funcs->DrawRectangle = r100DrawRectangle3D;
                    funcs->DrawLine      = r100DrawLine3D;
                    funcs->EmitCommands  = r100EmitCommands3D;
               } else {
                    funcs->FillRectangle = RADEON_FUNC(radeonFillRectangle2D);
                    funcs->FillTriangle  = NULL;
                    funcs->DrawRectangle = RADEON_FUNC(radeonDrawRectangle2D);
                    funcs->DrawLine      = RADEON_FUNC(radeonDrawLine2D);
                    funcs->EmitCommands  = NULL;
               }

               state->set = rdev->drawing_mask;
               break;

          case DFXL_BLIT:
          case DFXL_STRETCHBLIT:
          case DFXL_TEXTRIANGLES:     
               r100_set_source( rdrv, rdev, state );
               
               if (state->blittingflags & DSBLIT_MASK)
                    r100_set_source_mask( rdrv, rdev, state );
               
               if (state->blittingflags & DSBLIT_MODULATE_ALPHA)
                    r100_set_blend_function( rdrv, rdev, state );
               
               if (state->blittingflags & DSBLIT_MODULATE_COLOR)
                    r100_set_blitting_color( rdrv, rdev, state );
               
               if (state->blittingflags & DSBLIT_SRC_COLORKEY)
                    r100_set_src_colorkey( rdrv, rdev, state );
               
               r100_set_blittingflags( rdrv, rdev, state );

               if (RADEON_BLIT_3D()) {
                    funcs->Blit             = r100Blit3D;
                    funcs->StretchBlit      = r100StretchBlit;
                    funcs->TextureTriangles = r100TextureTriangles;
                    funcs->EmitCommands     = r100EmitCommands3D;
               } else {
                    funcs->Blit             = RADEON_FUNC(radeonBlit2D);
                    funcs->StretchBlit      = NULL;
                    funcs->TextureTriangles = NULL;
                    funcs->EmitCommands     = NULL;
               }
               
               state->set = (accel & DFXL_TEXTRIANGLES) 
                            ? : (rdev->blitting_mask & ~DFXL_TEXTRIANGLES);
               break;

          default:
               D_BUG( "unexpected drawing/blitting function" );
               break;
     }
     
     state->mod_hw = 0;
}

static void r200SetState( void *drv, void *dev,
                          GraphicsDeviceFuncs *funcs,
                          CardState *state, DFBAccelerationMask accel )
{
     RadeonDriverData *rdrv = (RadeonDriverData*) drv;
     RadeonDeviceData *rdev = (RadeonDeviceData*) dev;
 
     rdev->set &= ~state->mod_hw;
     if (DFB_BLITTING_FUNCTION( accel )) {
          if ((rdev->accel ^ accel) & DFXL_TEXTRIANGLES)
               rdev->set &= ~SMF_BLITTING_FLAGS;
     }
     
     rdev->accel = accel;
     
     r200_set_destination( rdrv, rdev, state );
     r200_set_clip( rdrv, rdev, state );
     r200_set_render_options( rdrv, rdev, state );
    
     switch (accel) {
          case DFXL_FILLRECTANGLE:
          case DFXL_FILLTRIANGLE:
          case DFXL_DRAWRECTANGLE:
          case DFXL_DRAWLINE:
               r200_set_drawing_color( rdrv, rdev, state );
               
               if (state->drawingflags & DSDRAW_BLEND)
                    r200_set_blend_function( rdrv, rdev, state );
               
               r200_set_drawingflags( rdrv, rdev, state );

               if (RADEON_DRAW_3D()) {
                    funcs->FillRectangle = r200FillRectangle3D;
                    funcs->FillTriangle  = r200FillTriangle;
                    funcs->DrawRectangle = r200DrawRectangle3D;
                    funcs->DrawLine      = r200DrawLine3D;
                    funcs->EmitCommands  = r200EmitCommands3D;
               } else {
                    funcs->FillRectangle = RADEON_FUNC(radeonFillRectangle2D);
                    funcs->FillTriangle  = NULL;
                    funcs->DrawRectangle = RADEON_FUNC(radeonDrawRectangle2D);
                    funcs->DrawLine      = RADEON_FUNC(radeonDrawLine2D);
                    funcs->EmitCommands  = NULL;
               }

               state->set = rdev->drawing_mask;
               break;

          case DFXL_BLIT:
          case DFXL_STRETCHBLIT:
          case DFXL_TEXTRIANGLES:     
               r200_set_source( rdrv, rdev, state );
               
               if (state->blittingflags & DSBLIT_MASK)
                    r200_set_source_mask( rdrv, rdev, state );
               
               if (state->blittingflags & DSBLIT_MODULATE_ALPHA)
                    r200_set_blend_function( rdrv, rdev, state );
               
               if (state->blittingflags & DSBLIT_MODULATE_COLOR)
                    r200_set_blitting_color( rdrv, rdev, state );
               
               if (state->blittingflags & DSBLIT_SRC_COLORKEY)
                    r200_set_src_colorkey( rdrv, rdev, state );
               
               r200_set_blittingflags( rdrv, rdev, state );
               
               if (RADEON_BLIT_3D()) {
                    funcs->Blit             = r200Blit3D;
                    funcs->StretchBlit      = r200StretchBlit;
                    funcs->TextureTriangles = r200TextureTriangles;
                    funcs->EmitCommands     = r200EmitCommands3D;
               } else {
                    funcs->Blit             = RADEON_FUNC(radeonBlit2D);
                    funcs->StretchBlit      = NULL;
                    funcs->TextureTriangles = NULL;
                    funcs->EmitCommands     = NULL;
               }
               
               state->set = (accel & DFXL_TEXTRIANGLES) 
                            ? : (rdev->blitting_mask & ~DFXL_TEXTRIANGLES);
               break;

          default:
               D_BUG( "unexpected drawing/blitting function" );
               break;
     }
     
     state->mod_hw = 0;
}

static void r300SetState( void *drv, void *dev,
                          GraphicsDeviceFuncs *funcs,
                          CardState *state, DFBAccelerationMask accel )
{
     RadeonDriverData *rdrv = (RadeonDriverData*) drv;
     RadeonDeviceData *rdev = (RadeonDeviceData*) dev;
 
     rdev->set &= ~state->mod_hw;
     if (DFB_BLITTING_FUNCTION( accel )) {
          if ((rdev->accel ^ accel) & DFXL_TEXTRIANGLES)
               rdev->set &= ~SMF_BLITTING_FLAGS;
     }
     
     rdev->accel = accel;
     
     r300_set_destination( rdrv, rdev, state );
     r300_set_clip( rdrv, rdev, state );
     r300_set_render_options( rdrv, rdev, state );
    
     switch (accel) {
          case DFXL_FILLRECTANGLE:
          case DFXL_FILLTRIANGLE:
          case DFXL_DRAWRECTANGLE:
          case DFXL_DRAWLINE:
               r300_set_drawing_color( rdrv, rdev, state );
               
               if (state->drawingflags & DSDRAW_BLEND)
                    r300_set_blend_function( rdrv, rdev, state );
               
               r300_set_drawingflags( rdrv, rdev, state );

               if (RADEON_DRAW_3D()) {  
                    funcs->FillRectangle = r300FillRectangle3D;
                    funcs->FillTriangle  = r300FillTriangle;
                    funcs->DrawRectangle = r300DrawRectangle3D;
                    funcs->DrawLine      = r300DrawLine3D;
                    funcs->EmitCommands  = r300EmitCommands3D;
               } else {
                    funcs->FillRectangle = RADEON_FUNC(radeonFillRectangle2D);
                    funcs->FillTriangle  = NULL;
                    funcs->DrawRectangle = RADEON_FUNC(radeonDrawRectangle2D);
                    funcs->DrawLine      = RADEON_FUNC(radeonDrawLine2D);
                    funcs->EmitCommands  = NULL;
               }

               state->set = rdev->drawing_mask;
               break;

          case DFXL_BLIT:
          case DFXL_STRETCHBLIT:
          case DFXL_TEXTRIANGLES:
               r300_set_source( rdrv, rdev, state );
               
               if (state->blittingflags & DSBLIT_MODULATE_ALPHA)
                    r300_set_blend_function( rdrv, rdev, state );
               
               if (state->blittingflags & DSBLIT_MODULATE_COLOR)
                    r300_set_blitting_color( rdrv, rdev, state );
               
               if (state->blittingflags & DSBLIT_SRC_COLORKEY)
                    r300_set_src_colorkey( rdrv, rdev, state );
               
               r300_set_blittingflags( rdrv, rdev, state );

               if (RADEON_BLIT_3D()) {
                    funcs->Blit             = r300Blit3D;
                    funcs->StretchBlit      = r300StretchBlit;
                    funcs->TextureTriangles = r300TextureTriangles;
                    funcs->EmitCommands     = r300EmitCommands3D;
               } else { 
                    funcs->Blit             = RADEON_FUNC(radeonBlit2D);
                    funcs->StretchBlit      = NULL;
                    funcs->TextureTriangles = NULL;
                    funcs->EmitCommands     = NULL;
               }
               
               state->set = (accel & DFXL_TEXTRIANGLES) 
                            ? : (rdev->blitting_mask & ~DFXL_TEXTRIANGLES);
               break;

          default:
               D_BUG( "unexpected drawing/blitting function" );
               break;
     }
     
     state->mod_hw = 0;
}


/* chipset detection */

static int 
radeon_find_chipset( RadeonDriverData *rdrv, int *ret_devid, int *ret_index )
{
     volatile u8   *mmio = rdrv->mmio_base;
     unsigned int   vendor_id;
     unsigned int   device_id;
     int            i;

     vendor_id = radeon_in16( mmio, CONFIG_VENDOR_ID );
     device_id = radeon_in16( mmio, CONFIG_DEVICE_ID );
     if (vendor_id != 0x1002 || !device_id)
          dfb_system_get_deviceid( &vendor_id, &device_id );
        
     if (vendor_id == 0x1002) {
          if (ret_devid)
               *ret_devid = device_id;        
          
          for (i = 0; i < D_ARRAY_SIZE( dev_table ); i++) {
               if ((unsigned int)dev_table[i].id == device_id) {
                    if (ret_index)
                         *ret_index = i;
                    return 1;
               }
          }
     }
     
     return 0;
}

/* exported symbols */

static int
driver_probe( CoreGraphicsDevice *device )
{
     switch (dfb_gfxcard_get_accelerator( device )) {
          case FB_ACCEL_ATI_RADEON:
               return 1;
          default:
               break;
     }

     return 0;
}

static void
driver_get_info( CoreGraphicsDevice *device,
                 GraphicsDriverInfo *info )
{
     /* fill driver info structure */
     snprintf( info->name,
               DFB_GRAPHICS_DRIVER_INFO_NAME_LENGTH,
               "ATI Radeon Driver" );

     snprintf( info->vendor,
               DFB_GRAPHICS_DRIVER_INFO_VENDOR_LENGTH,
               "Claudio Ciccani" );

     snprintf( info->license,
               DFB_GRAPHICS_DRIVER_INFO_LICENSE_LENGTH,
               "LGPL" );

     snprintf( info->url,
               DFB_GRAPHICS_DRIVER_INFO_URL_LENGTH,
               "http://www.directfb.org" );

     info->version.major = 1;
     info->version.minor = 2;

     info->driver_data_size = sizeof(RadeonDriverData);
     info->device_data_size = sizeof(RadeonDeviceData);
}

static DFBResult
driver_init_driver( CoreGraphicsDevice  *device,
                    GraphicsDeviceFuncs *funcs,
                    void                *driver_data,
                    void                *device_data,
                    CoreDFB             *core )
{
     RadeonDriverData    *rdrv = (RadeonDriverData*) driver_data;
     RadeonChipsetFamily  chip = CHIP_UNKNOWN;
     int                  idx;
     
     rdrv->device_data = (RadeonDeviceData*) device_data;
     
     /* gain access to memory mapped registers */
     rdrv->mmio_base = (volatile u8*) dfb_gfxcard_map_mmio( device, 0, 0x4000 );
     if (!rdrv->mmio_base)
          return DFB_IO;
     rdrv->mmio_size = 0x4000;
     
     rdrv->fb_base = dfb_gfxcard_memory_virtual( device, 0 );

     if (radeon_find_chipset( rdrv, NULL, &idx ))
          chip = dev_table[idx].chip;

     if (chip >= CHIP_R300 && !getenv( "R300_DISABLE_3D" )) {
          volatile void *base;
          /* increase amount of memory mapped registers */
          base = dfb_gfxcard_map_mmio( device, 0, 0x8000 );
          if (!base) {
               D_ERROR( "DirectFB/Radeon: You are running a buggy version of radeonfb!\n"
                        "     -> Please, apply the kernel patch named radeonfb-r300fix.\n" );
               D_INFO( "DirectFB/Radeon: 3D Acceleration will be disabled.\n" );
          }
          else {
               rdrv->mmio_base = base;
               rdrv->mmio_size = 0x8000;
          }
     }

     /* fill function table */
     funcs->AfterSetVar       = radeonAfterSetVar;
     funcs->EngineReset       = radeonEngineReset;
     funcs->EngineSync        = radeonEngineSync;
     funcs->InvalidateState   = radeonInvalidateState;
     funcs->FlushTextureCache = radeonFlushTextureCache;
#ifdef WORDS_BIGENDIAN
     funcs->SurfaceEnter      = radeonSurfaceEnter;
     funcs->SurfaceLeave      = radeonSurfaceLeave;
#endif
     
     if (chip >= CHIP_R300) {
          funcs->CheckState   = r300CheckState;
          funcs->SetState     = r300SetState;
     }
     else if (chip >= CHIP_R200) {
          funcs->CheckState   = r200CheckState;
          funcs->SetState     = r200SetState;
     }
     else if (chip >= CHIP_R100) {
          funcs->CheckState   = r100CheckState;
          funcs->SetState     = r100SetState;
     }
     
     /* primary screen */
     dfb_screens_hook_primary( device, driver_data, 
                               &RadeonCrtc1ScreenFuncs,
                               &OldPrimaryScreenFuncs,
                               &OldPrimaryScreenDriverData );
                               
     /* primary layer */
     dfb_layers_hook_primary( device, driver_data,
                              &RadeonCrtc1LayerFuncs,
                              &OldPrimaryLayerFuncs,
                              &OldPrimaryLayerDriverData );
                              
     /* overlay support */
     dfb_layers_register( dfb_screens_at( DSCID_PRIMARY ),
                          driver_data, &RadeonOverlayFuncs );
                          
     if (chip != CHIP_R100) {
          CoreScreen *screen;

          /* secondary screen support */
          screen = dfb_screens_register( device, driver_data,
                                         &RadeonCrtc2ScreenFuncs );
     
          /* secondary underlay support */     
          dfb_layers_register( screen, driver_data,
                               &RadeonCrtc2LayerFuncs );
         
          /* secondary overlay support */
          dfb_layers_register( screen, driver_data,
                               &RadeonOverlayFuncs );
     }
     
     return DFB_OK;
}

static DFBResult
driver_init_device( CoreGraphicsDevice *device,
                    GraphicsDeviceInfo *device_info,
                    void               *driver_data,
                    void               *device_data )
{
     RadeonDriverData *rdrv = (RadeonDriverData*) driver_data;
     RadeonDeviceData *rdev = (RadeonDeviceData*) device_data;
     volatile void    *mmio = rdrv->mmio_base;
     int               dev  = 0;
     int               idx  = 0;
     const char       *name = "Unknown";
     
     if (radeon_find_chipset( rdrv, &dev, &idx )) {
          rdev->chipset = dev_table[idx].chip;
          rdev->igp     = dev_table[idx].igp;
          name          = dev_table[idx].name;
     }
     else {
          if (!dev) {
               D_ERROR( "DirectFB/Radeon: Could not detect device id!\n"
                        "     -> Please, specify the bus location of"
                        "        the card by using the 'busid' option.\n" );
          }         
          D_INFO( "DirectFB/Radeon: "
                  "Unknown chipset, disabling acceleration!\n" );
     }
     
     /* fill device info */
     snprintf( device_info->name,
               DFB_GRAPHICS_DEVICE_INFO_NAME_LENGTH, 
               "%s (%04x)", name, dev );

     snprintf( device_info->vendor,
               DFB_GRAPHICS_DEVICE_INFO_VENDOR_LENGTH, "ATI" );

     device_info->caps.flags = CCF_CLIPPING | CCF_AUXMEMORY | CCF_RENDEROPTS;
     
     if (rdev->chipset >= CHIP_R300) {
          if (rdrv->mmio_size > 0x4000) {
               device_info->caps.accel    = R300_SUPPORTED_DRAWINGFUNCS |
                                            R300_SUPPORTED_BLITTINGFUNCS;
               device_info->caps.drawing  = R300_SUPPORTED_DRAWINGFLAGS;
               device_info->caps.blitting = R300_SUPPORTED_BLITTINGFLAGS;
          } else {
               device_info->caps.accel    = RADEON_SUPPORTED_2D_DRAWINGFUNCS |
                                            RADEON_SUPPORTED_2D_BLITTINGFUNCS;
               device_info->caps.drawing  = RADEON_SUPPORTED_2D_DRAWINGFLAGS;
               device_info->caps.blitting = RADEON_SUPPORTED_2D_BLITTINGFLAGS;
          }
     }
     else if (rdev->chipset >= CHIP_R200) {
          device_info->caps.accel    = R200_SUPPORTED_DRAWINGFUNCS |
                                       R200_SUPPORTED_BLITTINGFUNCS;
          device_info->caps.drawing  = R200_SUPPORTED_DRAWINGFLAGS;
          device_info->caps.blitting = R200_SUPPORTED_BLITTINGFLAGS;
     }
     else if (rdev->chipset >= CHIP_R100) {
          device_info->caps.accel    = R100_SUPPORTED_DRAWINGFUNCS |
                                       R100_SUPPORTED_BLITTINGFUNCS;
          device_info->caps.drawing  = R100_SUPPORTED_DRAWINGFLAGS;
          device_info->caps.blitting = R100_SUPPORTED_BLITTINGFLAGS;
     }

     device_info->limits.surface_byteoffset_alignment = 32;
     device_info->limits.surface_pixelpitch_alignment = 64;
     device_info->limits.surface_bytepitch_alignment  = 128;

     dfb_config->pollvsync_after = 1;

     /* reserve memory for YUV422 color buffer */
     rdev->yuv422_buffer = dfb_gfxcard_reserve_memory( device, 128 );
     if (rdev->yuv422_buffer == (u32)-1) {
          D_ERROR( "DirectFB/Radeon: "
                   "couldn't reserve 128 bytes of video memory!\n" );
          return DFB_NOVIDEOMEMORY;
     }

     rdev->fb_phys = dfb_gfxcard_memory_physical( device, 0 );

     radeon_waitidle( rdrv, rdev );

     /* get connected monitors */
     radeon_get_monitors( rdrv, rdev, &rdev->monitor1, &rdev->monitor2 );

     /* save the following regs */
     rdev->mc_fb_location = radeon_in32( mmio, MC_FB_LOCATION );
     rdev->mc_agp_location = radeon_in32( mmio, MC_AGP_LOCATION );  
     rdev->crtc_base_addr = radeon_in32( mmio, CRTC_BASE_ADDR );
     rdev->crtc2_base_addr = radeon_in32( mmio, CRTC2_BASE_ADDR );
     rdev->agp_base = radeon_in32( mmio, AGP_BASE );
     rdev->agp_cntl = radeon_in32( mmio, AGP_CNTL );
     rdev->aic_cntl = radeon_in32( mmio, AIC_CNTL );
     rdev->bus_cntl = radeon_in32( mmio, BUS_CNTL );
     rdev->fcp_cntl = radeon_in32( mmio, FCP_CNTL );
     rdev->cap0_trig_cntl = radeon_in32( mmio, CAP0_TRIG_CNTL );
     rdev->vid_buffer_control = radeon_in32( mmio, VID_BUFFER_CONTROL );
     rdev->display_test_debug_cntl = radeon_in32( mmio, DISPLAY_TEST_DEBUG_CNTL );
     rdev->surface_cntl = radeon_in32( mmio, SURFACE_CNTL ); 
     rdev->dp_gui_master_cntl = radeon_in32( mmio, DP_GUI_MASTER_CNTL );   
     
     rdev->surface_cntl_p = 
     rdev->surface_cntl_c = rdev->surface_cntl;
     
     if (rdev->igp) {
          u32 tom;
          /* force MC_FB_LOCATION to NB_TOM */
          tom = radeon_in32( mmio, NB_TOM );
          rdev->fb_offset = tom << 16;
          rdev->fb_size   = ((tom >> 16) - (tom & 0xffff) + 1) << 16;
     } 
     else {
          if (rdev->chipset >= CHIP_R300) {
               rdev->fb_offset = 0;
               rdev->fb_size   = radeon_in32( mmio, CONFIG_MEMSIZE );
          } else {
               rdev->fb_offset = radeon_in32( mmio, CONFIG_APER_0_BASE ); 
               rdev->fb_size   = radeon_in32( mmio, CONFIG_APER_SIZE );
          }
     }
     
     radeon_out32( mmio, MC_FB_LOCATION, (rdev->fb_offset>>16) |
                        ((rdev->fb_offset + rdev->fb_size - 1) & 0xffff0000) ); 

     D_DEBUG( "DirectFB/Radeon: "
              "Framebuffer located at 0x%08x:0x%08x.\n",
              rdev->fb_offset, rdev->fb_offset + rdev->fb_size - 1 );
 
     if (dfb_system_auxram_length()) { 
          rdev->agp_offset = (rdev->fb_offset + rdev->fb_size) & 0xffc00000;
          rdev->agp_size   = dfb_system_auxram_length();
          
          /* enable AGP support */ 
          radeon_out32( mmio, AIC_CNTL, rdev->aic_cntl & ~PCIGART_TRANSLATE_EN );
          radeon_out32( mmio, AGP_BASE, dfb_system_aux_memory_physical( 0 ) );
          radeon_out32( mmio, AGP_CNTL, rdev->agp_cntl | 0x000e0000 );
          radeon_out32( mmio, BUS_CNTL, rdev->bus_cntl & ~BUS_MASTER_DIS );

          radeon_out32( mmio, MC_AGP_LOCATION, (rdev->agp_offset>>16) |
                        ((rdev->agp_offset + rdev->agp_size - 1) & 0xffff0000) );
          
          D_DEBUG( "DirectFB/Radeon: "
                   "AGP Aperture located at 0x%08x:0x%08x.\n",
                   rdev->agp_offset, rdev->agp_offset + rdev->agp_size - 1 );
     }

     radeon_out32( mmio, CRTC_BASE_ADDR, rdev->fb_offset );
     radeon_out32( mmio, DISP_MERGE_CNTL, 0xffff0000 );
     if (rdev->chipset != CHIP_R100) {
          radeon_out32( mmio, CRTC2_BASE_ADDR, rdev->fb_offset );
          radeon_out32( mmio, DISP2_MERGE_CNTL, 0xffff0000 );
     }

     radeon_out32( mmio, FCP_CNTL, FCP0_SRC_GND );
     radeon_out32( mmio, CAP0_TRIG_CNTL, 0 );
     radeon_out32( mmio, VID_BUFFER_CONTROL, 0x00010001 );
     radeon_out32( mmio, DISPLAY_TEST_DEBUG_CNTL, 0 );
 
     radeon_reset( rdrv, rdev );
         
     return DFB_OK;
}

static void
driver_close_device( CoreGraphicsDevice *device,
                     void               *driver_data,
                     void               *device_data )
{
     RadeonDriverData *rdrv = (RadeonDriverData*) driver_data;
     RadeonDeviceData *rdev = (RadeonDeviceData*) device_data;
     volatile u8      *mmio = rdrv->mmio_base;
    
     D_DEBUG( "DirectFB/Radeon: FIFO Performance Monitoring:\n" );
     D_DEBUG( "DirectFB/Radeon:  %9d radeon_waitfifo calls\n",
              rdev->waitfifo_calls );
     D_DEBUG( "DirectFB/Radeon:  %9d register writes (radeon_waitfifo sum)\n",
              rdev->waitfifo_sum );
     D_DEBUG( "DirectFB/Radeon:  %9d FIFO wait cycles (depends on CPU)\n",
              rdev->fifo_waitcycles );
     D_DEBUG( "DirectFB/Radeon:  %9d IDLE wait cycles (depends on CPU)\n",
              rdev->idle_waitcycles );
     D_DEBUG( "DirectFB/Radeon:  %9d FIFO space cache hits(depends on CPU)\n",
              rdev->fifo_cache_hits );
     D_DEBUG( "DirectFB/Radeon: Conclusion:\n" );
     D_DEBUG( "DirectFB/Radeon:  Average register writes/radeon_waitfifo call:%.2f\n",
              rdev->waitfifo_sum / (float)rdev->waitfifo_calls );
     D_DEBUG( "DirectFB/Radeon:  Average wait cycles/radeon_waitfifo call: %.2f\n",
              rdev->fifo_waitcycles / (float)rdev->waitfifo_calls );
     D_DEBUG( "DirectFB/Radeon:  Average fifo space cache hits: %02d%%\n",
              (int)(100 * rdev->fifo_cache_hits / (float)rdev->waitfifo_calls) );

     radeon_reset( rdrv, rdev );

     /* restore previously saved regs */
     radeon_out32( mmio, MC_FB_LOCATION, rdev->mc_fb_location );
     radeon_out32( mmio, MC_AGP_LOCATION, rdev->mc_agp_location ); 
     radeon_out32( mmio, CRTC_BASE_ADDR, rdev->crtc_base_addr );
     radeon_out32( mmio, CRTC2_BASE_ADDR, rdev->crtc2_base_addr );
     radeon_out32( mmio, AGP_CNTL, rdev->agp_cntl );
     radeon_out32( mmio, AGP_BASE, rdev->agp_base );
     radeon_out32( mmio, AIC_CNTL, rdev->aic_cntl );
     radeon_out32( mmio, BUS_CNTL, rdev->bus_cntl );
     radeon_out32( mmio, FCP_CNTL, rdev->fcp_cntl );
     radeon_out32( mmio, CAP0_TRIG_CNTL, rdev->cap0_trig_cntl );
     radeon_out32( mmio, VID_BUFFER_CONTROL, rdev->vid_buffer_control );
     radeon_out32( mmio, DISPLAY_TEST_DEBUG_CNTL, rdev->display_test_debug_cntl );
     radeon_out32( mmio, SURFACE_CNTL, rdev->surface_cntl );
     
     radeon_waitfifo( rdrv, rdev, 3 );
     radeon_out32( mmio, SC_TOP_LEFT, 0 );
     radeon_out32( mmio, DEFAULT_SC_BOTTOM_RIGHT, DEFAULT_SC_RIGHT_MAX |
                                                  DEFAULT_SC_BOTTOM_MAX );
     radeon_out32( mmio, DP_GUI_MASTER_CNTL, rdev->dp_gui_master_cntl );
}

static void
driver_close_driver( CoreGraphicsDevice *device,
                     void               *driver_data )
{
    RadeonDriverData *rdrv = (RadeonDriverData*) driver_data;

    dfb_gfxcard_unmap_mmio( device, rdrv->mmio_base, rdrv->mmio_size );
}

