/*
 * 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 <directfb.h>

#include <direct/messages.h>

#include <core/coredefs.h>
#include <core/coretypes.h>

#include <core/state.h>
#include <core/gfxcard.h>
#include <core/surface.h>

#include <gfx/convert.h>

#include "radeon.h"
#include "radeon_regs.h"
#include "radeon_mmio.h"
#include "radeon_state.h"


static const u32 r200SrcBlend[] = {
     SRC_BLEND_GL_ZERO,                 // DSBF_ZERO
     SRC_BLEND_GL_ONE,                  // DSBF_ONE
     SRC_BLEND_GL_SRC_COLOR,            // DSBF_SRCCOLOR
     SRC_BLEND_GL_ONE_MINUS_SRC_COLOR,  // DSBF_INVSRCCOLOR
     SRC_BLEND_GL_SRC_ALPHA,            // DSBF_SRCALPHA
     SRC_BLEND_GL_ONE_MINUS_SRC_ALPHA,  // DSBF_INVSRCALPHA
     SRC_BLEND_GL_DST_ALPHA,            // DSBF_DSTALPHA
     SRC_BLEND_GL_ONE_MINUS_DST_ALPHA,  // DSBF_INVDSTALPHA
     SRC_BLEND_GL_DST_COLOR,            // DSBF_DSTCOLOR
     SRC_BLEND_GL_ONE_MINUS_DST_COLOR,  // DSBF_INVDSTCOLOR
     SRC_BLEND_GL_SRC_ALPHA_SATURATE    // DSBF_SRCALPHASAT
};

static const u32 r200DstBlend[] = {
     DST_BLEND_GL_ZERO,                 // DSBF_ZERO
     DST_BLEND_GL_ONE,                  // DSBF_ONE
     DST_BLEND_GL_SRC_COLOR,            // DSBF_SRCCOLOR
     DST_BLEND_GL_ONE_MINUS_SRC_COLOR,  // DSBF_INVSRCCOLOR
     DST_BLEND_GL_SRC_ALPHA,            // DSBF_SRCALPHA
     DST_BLEND_GL_ONE_MINUS_SRC_ALPHA,  // DSBF_INVSRCALPHA
     DST_BLEND_GL_DST_ALPHA,            // DSBF_DSTALPHA
     DST_BLEND_GL_ONE_MINUS_DST_ALPHA,  // DSBF_INVDSTALPHA
     DST_BLEND_GL_DST_COLOR,            // DSBF_DSTCOLOR
     DST_BLEND_GL_ONE_MINUS_DST_COLOR,  // DSBF_INVDSTCOLOR
     DST_BLEND_GL_ZERO                  // DSBF_SRCALPHASAT
};


void r200_restore( RadeonDriverData *rdrv, RadeonDeviceData *rdev )
{
     volatile u8 *mmio = rdrv->mmio_base;
     
     radeon_waitfifo( rdrv, rdev, 15 );
     /* enable caches */
     radeon_out32( mmio, RB2D_DSTCACHE_MODE, RB2D_DC_2D_CACHE_AUTOFLUSH     |
                                             RB2D_DC_3D_CACHE_AUTOFLUSH     |
                                             R200_RB2D_DC_2D_CACHE_AUTOFREE |
                                             R200_RB2D_DC_3D_CACHE_AUTOFREE );
     radeon_out32( mmio, RB3D_DSTCACHE_MODE, RB3D_DC_2D_CACHE_AUTOFLUSH     |
                                             RB3D_DC_3D_CACHE_AUTOFLUSH     |
                                             R200_RB3D_DC_2D_CACHE_AUTOFREE |
                                             R200_RB3D_DC_3D_CACHE_AUTOFREE );          
     /* restore 3d engine state */
     radeon_out32( mmio, SE_LINE_WIDTH, 0x10 );
     radeon_out32( mmio, RE_POINTSIZE, 0x10 );
     radeon_out32( mmio, PP_MISC, ALPHA_TEST_PASS ); 
     radeon_out32( mmio, R200_PP_CNTL_X, 0 );
     radeon_out32( mmio, R200_PP_TXMULTI_CTL_0, 0 ); 
     radeon_out32( mmio, R200_RE_CNTL, R200_SCISSOR_ENABLE );
     radeon_out32( mmio, R200_SE_VTX_STATE_CNTL, 0 );
     radeon_out32( mmio, R200_SE_VAP_CNTL, R200_VAP_VF_MAX_VTX_NUM |
                                           R200_VAP_FORCE_W_TO_ONE );
#ifdef WORDS_BIGENDIAN
     radeon_out32( mmio, R200_SE_VAP_CNTL_STATUS, R200_TCL_BYPASS | R200_VC_32BIT_SWAP );
#else
     radeon_out32( mmio, R200_SE_VAP_CNTL_STATUS, R200_TCL_BYPASS );
#endif
     radeon_out32( mmio, RB3D_ZSTENCILCNTL, Z_TEST_ALWAYS );
     radeon_out32( mmio, RB3D_ROPCNTL, ROP_XOR );
     radeon_out32( mmio, R200_PP_TXFORMAT_X_0, 0 );
     radeon_out32( mmio, R200_PP_TXFORMAT_X_1, 0 );
}

void r200_set_destination( RadeonDriverData *rdrv,
                           RadeonDeviceData *rdev,
                           CardState        *state )
{
     CoreSurface       *surface = state->destination;
     CoreSurfaceBuffer *buffer  = state->dst.buffer;
     volatile u8       *mmio    = rdrv->mmio_base;
     u32                offset;
     u32                pitch;
    
     if (RADEON_IS_SET( DESTINATION ))
          return;
     
     D_ASSERT( (state->dst.offset % 32) == 0 );
     D_ASSERT( (state->dst.pitch % 32) == 0 );

     offset = radeon_buffer_offset( rdev, &state->dst );
     pitch  = state->dst.pitch;
    
     if (rdev->dst_offset != offset        ||
         rdev->dst_pitch  != pitch         ||
         rdev->dst_format != buffer->format)
     {
          bool dst_422 = false;
          
          switch (buffer->format) {
               case DSPF_LUT8:
               case DSPF_ALUT44:
               case DSPF_A8:
                    rdev->gui_master_cntl = GMC_DST_8BPP;
                    rdev->rb3d_cntl = COLOR_FORMAT_RGB8;
                    break;
               case DSPF_RGB332:          
                    rdev->gui_master_cntl = GMC_DST_8BPP;
                    rdev->rb3d_cntl = COLOR_FORMAT_RGB332 | DITHER_ENABLE;
                    break;
               case DSPF_ARGB2554:
                    rdev->gui_master_cntl = GMC_DST_16BPP;
                    rdev->rb3d_cntl = COLOR_FORMAT_RGB565;
                    break;
               case DSPF_RGB444:
               case DSPF_ARGB4444:
                    rdev->gui_master_cntl = GMC_DST_16BPP;
                    rdev->rb3d_cntl = COLOR_FORMAT_ARGB4444 | DITHER_ENABLE;
                    break;
               case DSPF_RGB555:
               case DSPF_ARGB1555:          
                    rdev->gui_master_cntl = GMC_DST_15BPP;
                    rdev->rb3d_cntl = COLOR_FORMAT_ARGB1555 | DITHER_ENABLE;
                    break;
               case DSPF_RGB16:
                    rdev->gui_master_cntl = GMC_DST_16BPP;
                    rdev->rb3d_cntl = COLOR_FORMAT_RGB565 | DITHER_ENABLE;
                    break;
               case DSPF_RGB32:
               case DSPF_ARGB:
               case DSPF_AiRGB:
               case DSPF_AYUV:
                    rdev->gui_master_cntl = GMC_DST_32BPP;
                    rdev->rb3d_cntl = COLOR_FORMAT_ARGB8888;
                    break;
               case DSPF_UYVY:
                    rdev->gui_master_cntl = GMC_DST_YVYU;
                    rdev->rb3d_cntl = COLOR_FORMAT_YUV422_YVYU;
                    dst_422 = true;
                    break;
               case DSPF_YUY2:
                    rdev->gui_master_cntl = GMC_DST_VYUY;
                    rdev->rb3d_cntl = COLOR_FORMAT_YUV422_VYUY;
                    dst_422 = true;
                    break;
               case DSPF_I420:
                    rdev->gui_master_cntl = GMC_DST_8BPP;
                    rdev->rb3d_cntl = COLOR_FORMAT_RGB8;
                    rdev->dst_offset_cb = offset + pitch * surface->config.size.h;
                    rdev->dst_offset_cr = rdev->dst_offset_cb + 
                                          pitch/2 * surface->config.size.h/2;
                    break;
               case DSPF_YV12:
                    rdev->gui_master_cntl = GMC_DST_8BPP;
                    rdev->rb3d_cntl = COLOR_FORMAT_RGB8;
                    rdev->dst_offset_cr = offset + pitch * surface->config.size.h;
                    rdev->dst_offset_cb = rdev->dst_offset_cr +
                                          pitch/2 * surface->config.size.h/2;
                    break;
               default:
                    D_BUG( "unexpected pixelformat" );
                    return;
          }

          rdev->gui_master_cntl |= GMC_DP_SRC_SOURCE_MEMORY  |
                                   GMC_WR_MSK_DIS            |
                                   GMC_SRC_PITCH_OFFSET_CNTL |
                                   GMC_DST_PITCH_OFFSET_CNTL |
                                   GMC_DST_CLIPPING;
          
          radeon_waitfifo( rdrv, rdev, 4 ); 
          radeon_out32( mmio, DST_OFFSET, offset );
          radeon_out32( mmio, DST_PITCH, pitch );
          radeon_out32( mmio, RB3D_COLOROFFSET, offset );
          radeon_out32( mmio, RB3D_COLORPITCH,  
                              pitch / DFB_BYTES_PER_PIXEL(buffer->format) );
          
          if (rdev->dst_format != buffer->format) {
               if (dst_422 && !rdev->dst_422) {
                    RADEON_UNSET( SOURCE );
                    RADEON_UNSET( CLIP );
               }
               
               RADEON_UNSET( COLOR );
               RADEON_UNSET( DST_BLEND );
          }
          
          rdev->dst_format = buffer->format;
          rdev->dst_offset = offset;
          rdev->dst_pitch  = pitch;
          rdev->dst_422    = dst_422;
     }

     RADEON_SET( DESTINATION );
}

void r200_set_source( RadeonDriverData *rdrv,
                      RadeonDeviceData *rdev,
                      CardState        *state )
{
     CoreSurface       *surface  = state->source;
     CoreSurfaceBuffer *buffer   = state->src.buffer;
     volatile u8       *mmio     = rdrv->mmio_base;
     u32                txformat = R200_TXFORMAT_NON_POWER2;
     u32                txfilter = R200_MAG_FILTER_LINEAR  |
                                   R200_MIN_FILTER_LINEAR  |
                                   R200_CLAMP_S_CLAMP_LAST |
                                   R200_CLAMP_T_CLAMP_LAST;
     
     if (RADEON_IS_SET( SOURCE )) {
          if ((state->blittingflags & DSBLIT_DEINTERLACE) ==
              (rdev->blittingflags  & DSBLIT_DEINTERLACE))
               return;
     }

     D_ASSERT( (state->src.offset % 32) == 0 );
     D_ASSERT( (state->src.pitch % 32) == 0 );
     
     rdev->src_offset = radeon_buffer_offset( rdev, &state->src );
     rdev->src_pitch  = state->src.pitch;
     rdev->src_width  = surface->config.size.w;
     rdev->src_height = surface->config.size.h;

     switch (buffer->format) {
          case DSPF_LUT8:
               txformat |= R200_TXFORMAT_I8;
               txfilter &= ~(R200_MAG_FILTER_LINEAR |
                             R200_MIN_FILTER_LINEAR);
               rdev->src_mask = 0x000000ff;
               break;
          case DSPF_ALUT44:
               txformat |= R200_TXFORMAT_I8;
               txfilter &= ~(R200_MAG_FILTER_LINEAR |
                             R200_MIN_FILTER_LINEAR);
               rdev->src_mask = 0x0000000f;
               break;
          case DSPF_A8:
               txformat |= R200_TXFORMAT_I8 |
                           R200_TXFORMAT_ALPHA_IN_MAP;
               rdev->src_mask = 0;
               break;
          case DSPF_RGB332:
               txformat |= R200_TXFORMAT_RGB332;
               rdev->src_mask = 0x000000ff;
               break;
          case DSPF_ARGB2554:
               txformat |= R200_TXFORMAT_RGB565;
               txfilter &= ~(R200_MAG_FILTER_LINEAR |
                             R200_MIN_FILTER_LINEAR);
               rdev->src_mask = 0x00003fff;
               break;
          case DSPF_RGB444:
               txformat |= R200_TXFORMAT_ARGB4444;
               rdev->src_mask = 0x00000fff;
               break;     
          case DSPF_ARGB4444:
               txformat |= R200_TXFORMAT_ARGB4444 |
                           R200_TXFORMAT_ALPHA_IN_MAP;
               rdev->src_mask = 0x00000fff;
               break;
          case DSPF_RGB555:
               txformat |= R200_TXFORMAT_ARGB1555;
               rdev->src_mask = 0x00007fff;
               break; 
          case DSPF_ARGB1555:
               txformat |= R200_TXFORMAT_ARGB1555 |
                           R200_TXFORMAT_ALPHA_IN_MAP;
               rdev->src_mask = 0x00007fff;
               break;
          case DSPF_RGB16:
               txformat |= R200_TXFORMAT_RGB565;
               rdev->src_mask = 0x0000ffff;
               break;
          case DSPF_RGB32:
               txformat |= R200_TXFORMAT_ARGB8888;
               rdev->src_mask = 0x00ffffff;
               break;
          case DSPF_ARGB:
          case DSPF_AiRGB:
          case DSPF_AYUV:
               txformat |= R200_TXFORMAT_ARGB8888 |
                           R200_TXFORMAT_ALPHA_IN_MAP;
               rdev->src_mask = 0x00ffffff;
               break;
          case DSPF_UYVY:
               txformat |= R200_TXFORMAT_YVYU422;
               if (!rdev->dst_422)
                    txfilter |= R200_YUV_TO_RGB;
               rdev->src_mask = 0xffffffff;
               break;
          case DSPF_YUY2:
               txformat |= R200_TXFORMAT_VYUY422;
               if (!rdev->dst_422)
                    txfilter |= R200_YUV_TO_RGB;
               rdev->src_mask = 0xffffffff;
               break;
          case DSPF_I420:
               txformat |= R200_TXFORMAT_I8;
               rdev->src_offset_cb = rdev->src_offset +
                                     rdev->src_pitch * rdev->src_height;
               rdev->src_offset_cr = rdev->src_offset_cb +
                                     rdev->src_pitch/2 * rdev->src_height/2;
               rdev->src_mask = 0x000000ff;
               break;
          case DSPF_YV12:
               txformat |= R200_TXFORMAT_I8;
               rdev->src_offset_cr = rdev->src_offset +
                                     rdev->src_pitch * rdev->src_height;
               rdev->src_offset_cb = rdev->src_offset_cr +
                                     rdev->src_pitch/2 * rdev->src_height/2;
               rdev->src_mask = 0x000000ff;
               break;
          default:
               D_BUG( "unexpected pixelformat" );
               return;
     }

     if (state->blittingflags & DSBLIT_DEINTERLACE) { 
          rdev->src_height /= 2;
          if (surface->config.caps & DSCAPS_SEPARATED) {
               if (surface->field) {
                    rdev->src_offset    += rdev->src_height * rdev->src_pitch;
                    rdev->src_offset_cr += rdev->src_height * rdev->src_pitch/4;
                    rdev->src_offset_cb += rdev->src_height * rdev->src_pitch/4;
               }
          } else {
               if (surface->field) {
                    rdev->src_offset    += rdev->src_pitch;
                    rdev->src_offset_cr += rdev->src_pitch/2;
                    rdev->src_offset_cb += rdev->src_pitch/2;
               }
               rdev->src_pitch *= 2;
          }
     }

     radeon_waitfifo( rdrv, rdev, 7 );
     radeon_out32( mmio, SRC_OFFSET, rdev->src_offset );
     radeon_out32( mmio, SRC_PITCH, rdev->src_pitch );
     radeon_out32( mmio, R200_PP_TXFILTER_0, txfilter );
     radeon_out32( mmio, R200_PP_TXFORMAT_0, txformat );
     radeon_out32( mmio, R200_PP_TXSIZE_0, ((rdev->src_height-1) << 16) | 
                                           ((rdev->src_width-1) & 0xffff) );
     radeon_out32( mmio, R200_PP_TXPITCH_0, rdev->src_pitch - 32 );
     radeon_out32( mmio, R200_PP_TXOFFSET_0, rdev->src_offset );

     if (rdev->src_format != buffer->format)
          RADEON_UNSET( BLITTING_FLAGS );
     rdev->src_format = buffer->format;

     RADEON_SET( SOURCE );
}

void r200_set_source_mask( RadeonDriverData *rdrv,
                           RadeonDeviceData *rdev,
                           CardState        *state )
{
     CoreSurface       *surface  = state->source_mask;
     CoreSurfaceBuffer *buffer   = state->src_mask.buffer;
     volatile u8       *mmio     = rdrv->mmio_base;
     u32                txformat = R200_TXFORMAT_NON_POWER2;
     u32                txfilter = R200_MAG_FILTER_LINEAR  |
                                   R200_MIN_FILTER_LINEAR  |
                                   R200_CLAMP_S_CLAMP_LAST |
                                   R200_CLAMP_T_CLAMP_LAST;
     
     if (RADEON_IS_SET( SOURCE_MASK )) {
          if ((state->blittingflags & DSBLIT_DEINTERLACE) ==
              (rdev->blittingflags  & DSBLIT_DEINTERLACE))
               return;
     }

     D_ASSERT( (state->src_mask.offset % 32) == 0 );
     D_ASSERT( (state->src_mask.pitch % 32) == 0 );
     
     rdev->msk_format = buffer->format;
     rdev->msk_offset = radeon_buffer_offset( rdev, &state->src_mask );
     rdev->msk_pitch  = state->src_mask.pitch;
     rdev->msk_width  = surface->config.size.w;
     rdev->msk_height = surface->config.size.h;

     switch (buffer->format) {
          case DSPF_A8:
               txformat |= R200_TXFORMAT_I8 |
                           R200_TXFORMAT_ALPHA_IN_MAP;
               break;
          case DSPF_RGB332:
               txformat |= R200_TXFORMAT_RGB332;
               break;
          case DSPF_RGB444:
               txformat |= R200_TXFORMAT_ARGB4444;
               break;     
          case DSPF_ARGB4444:
               txformat |= R200_TXFORMAT_ARGB4444 |
                           R200_TXFORMAT_ALPHA_IN_MAP;
               break;
          case DSPF_RGB555:
               txformat |= R200_TXFORMAT_ARGB1555;
               break; 
          case DSPF_ARGB1555:
               txformat |= R200_TXFORMAT_ARGB1555 |
                           R200_TXFORMAT_ALPHA_IN_MAP;
               break;
          case DSPF_RGB16:
               txformat |= R200_TXFORMAT_RGB565;
               break;
          case DSPF_RGB32:
               txformat |= R200_TXFORMAT_ARGB8888;
               break;
          case DSPF_ARGB:
          case DSPF_AiRGB:
               txformat |= R200_TXFORMAT_ARGB8888 |
                           R200_TXFORMAT_ALPHA_IN_MAP;
               break;
          default:
               D_BUG( "unexpected pixelformat" );
               return;
     }

     if (state->blittingflags & DSBLIT_DEINTERLACE) { 
          rdev->msk_height /= 2;
          if (surface->config.caps & DSCAPS_SEPARATED) {
               if (surface->field)
                    rdev->msk_offset += rdev->msk_height * rdev->msk_pitch;
          } else {
               if (surface->field)
                    rdev->msk_offset += rdev->msk_pitch;
               rdev->msk_pitch *= 2;
          }
     }

     radeon_waitfifo( rdrv, rdev, 5 );
     radeon_out32( mmio, R200_PP_TXFILTER_1, txfilter );
     radeon_out32( mmio, R200_PP_TXFORMAT_1, txformat );
     radeon_out32( mmio, R200_PP_TXSIZE_1, ((rdev->msk_height-1) << 16) | 
                                           ((rdev->msk_width-1) & 0xffff) );
     radeon_out32( mmio, R200_PP_TXPITCH_1, rdev->msk_pitch - 32 );
     radeon_out32( mmio, R200_PP_TXOFFSET_1, rdev->msk_offset );

     RADEON_SET( SOURCE_MASK );
}

void r200_set_clip( RadeonDriverData *rdrv,
                    RadeonDeviceData *rdev,
                    CardState        *state )
{
     DFBRegion   *clip = &state->clip;
     volatile u8 *mmio = rdrv->mmio_base;
     
     if (RADEON_IS_SET( CLIP ))
          return;
  
     /* 2d clip */
     radeon_waitfifo( rdrv, rdev, 2 );
     if (rdev->dst_422) {
          radeon_out32( mmio, SC_TOP_LEFT,
                      (clip->y1 << 16) | (clip->x1/2 & 0xffff) );
          radeon_out32( mmio, SC_BOTTOM_RIGHT,
                      ((clip->y2+1) << 16) | ((clip->x2+1)/2 & 0xffff) );
     } else {     
          radeon_out32( mmio, SC_TOP_LEFT, 
                      (clip->y1 << 16) | (clip->x1 & 0xffff) );
          radeon_out32( mmio, SC_BOTTOM_RIGHT,
                      ((clip->y2+1) << 16) | ((clip->x2+1) & 0xffff) );
     }
      
     /* 3d clip */
     radeon_waitfifo( rdrv, rdev, 2 );
     radeon_out32( mmio, RE_TOP_LEFT, 
                 (clip->y1 << 16) | (clip->x1 & 0xffff) );
     radeon_out32( mmio, RE_BOTTOM_RIGHT,
                 (clip->y2 << 16) | (clip->x2 & 0xffff) );
     
     rdev->clip = state->clip;
     
     RADEON_SET( CLIP );
}

#define R200_SET_YUV422_COLOR( rdrv, rdev, y, u, v ) {          \
     radeon_out32( (rdrv)->fb_base, (rdev)->yuv422_buffer,      \
                   PIXEL_YUY2( y, u, v ) );                     \
     radeon_in8( (rdrv)->fb_base, (rdev)->yuv422_buffer );      \
     radeon_waitfifo( rdrv, rdev, 3 );                          \
     radeon_out32( (rdrv)->mmio_base, R200_PP_TXOFFSET_1,       \
                   (rdev)->fb_offset + (rdev)->yuv422_buffer ); \
     radeon_out32( (rdrv)->mmio_base, R200_PP_TXFORMAT_1,       \
                   R200_TXFORMAT_VYUY422 );                     \
     radeon_out32( (rdrv)->mmio_base, R200_PP_TXFILTER_1, 0 );  \
}

void r200_set_drawing_color( RadeonDriverData *rdrv,
                             RadeonDeviceData *rdev,
                             CardState        *state )
{
     DFBColor color   = state->color;
     int      index   = state->color_index;
     u32      color2d;
     u32      color3d;
     int      y, u, v;

     if (RADEON_IS_SET( COLOR ) && RADEON_IS_SET( DRAWING_FLAGS ))
          return;

     if (state->drawingflags & DSDRAW_SRC_PREMULTIPLY) {
          color.r = ((long) color.r * color.a / 255L);
          color.g = ((long) color.g * color.a / 255L);
          color.b = ((long) color.b * color.a / 255L);
     }

     color3d = PIXEL_ARGB( color.a, color.r,
                           color.g, color.b );
 
     switch (rdev->dst_format) {
          case DSPF_ALUT44:
               index |= (color.a & 0xf0);
          case DSPF_LUT8:
               color2d = index;
               color3d = PIXEL_RGB32( index, index, index );
               break;
          case DSPF_A8:
               color2d = color.a;
               color3d = (color.a << 24) | 0x00ffffff;              
               break;
          case DSPF_RGB332:
               color2d = PIXEL_RGB332( color.r, color.g, color.b );
               break;
          case DSPF_ARGB2554:
               color2d = PIXEL_ARGB2554( color.a, color.r,
                                         color.g, color.b );
               break;
          case DSPF_RGB444:
          case DSPF_ARGB4444:
               color2d = PIXEL_ARGB4444( color.a, color.r,
                                         color.g, color.b );
               break;
          case DSPF_RGB555:
          case DSPF_ARGB1555:
               color2d = PIXEL_ARGB1555( color.a, color.r,
                                         color.g, color.b );
               break;
          case DSPF_RGB16:
               color2d = PIXEL_RGB16( color.r, color.g, color.b );
               break;
          case DSPF_RGB32:
               color2d = PIXEL_RGB32( color.r, color.g, color.b );
               break;
          case DSPF_ARGB:
               color2d = PIXEL_ARGB( color.a, color.r,
                                     color.g, color.b );
               break;
          case DSPF_AiRGB:
               color2d = PIXEL_AiRGB( color.a, color.r,
                                      color.g, color.b );
               break;
          case DSPF_AYUV:
               RGB_TO_YCBCR( color.r, color.g, color.b, y, u, v );
               color3d = color2d = PIXEL_AYUV( color.a, y, u, v );
               break;
          case DSPF_UYVY:
               RGB_TO_YCBCR( color.r, color.g, color.b, y, u, v );
               color2d = PIXEL_UYVY( y, u, v );
               R200_SET_YUV422_COLOR( rdrv, rdev, y, u, v );
               break;
          case DSPF_YUY2:
               RGB_TO_YCBCR( color.r, color.g, color.b, y, u, v );
               color2d = PIXEL_YUY2( y, u, v );
               R200_SET_YUV422_COLOR( rdrv, rdev, y, u, v );
               break;
          case DSPF_I420:
          case DSPF_YV12:
               RGB_TO_YCBCR( color.r, color.g, color.b, y, u, v );
               rdev->y_cop  = PIXEL_ARGB( color.a, y, y, y );
               rdev->cb_cop = PIXEL_ARGB( color.a, u, u, u );
               rdev->cr_cop = PIXEL_ARGB( color.a, v, v, v );
               color3d = color2d = rdev->y_cop;
               break;
          default:
               D_BUG( "unexpected pixelformat" );
               color2d = 0;
               break;
     }
     
     radeon_waitfifo( rdrv, rdev, 2 );
     radeon_out32( rdrv->mmio_base, DP_BRUSH_FRGD_CLR, color2d );
     radeon_out32( rdrv->mmio_base, R200_PP_TFACTOR_1, color3d );

     RADEON_SET( COLOR );
}

void r200_set_blitting_color( RadeonDriverData *rdrv,
                              RadeonDeviceData *rdev,
                              CardState        *state )
{
     DFBColor color   = state->color;
     u32      color3d;
     int      y, u, v;
     
     if (RADEON_IS_SET( COLOR ) && RADEON_IS_SET( BLITTING_FLAGS ))
          return;

     if (state->blittingflags & DSBLIT_COLORIZE &&
         state->blittingflags & DSBLIT_SRC_PREMULTCOLOR) {
          color.r = color.r * color.a / 255;
          color.g = color.g * color.a / 255;
          color.b = color.b * color.a / 255;
     }

     switch (rdev->dst_format) {
          case DSPF_A8:
               color3d = (color.a << 24) | 0x00ffffff;
               break;
          case DSPF_I420:
          case DSPF_YV12: 
               RGB_TO_YCBCR( color.r, color.g, color.b, y, u, v );
               rdev->y_cop  = PIXEL_ARGB( color.a, y, y, y );
               rdev->cb_cop = PIXEL_ARGB( color.a, u, u, u );
               rdev->cr_cop = PIXEL_ARGB( color.a, v, v, v );
               color3d = rdev->y_cop;
               break;
          case DSPF_AYUV:
               RGB_TO_YCBCR( color.r, color.g, color.b, y, u, v );
               color3d = PIXEL_AYUV( color.a, y, u, v );
               break;
          case DSPF_UYVY:
          case DSPF_YUY2:
               RGB_TO_YCBCR( color.r, color.g, color.b, y, u, v );
               R200_SET_YUV422_COLOR( rdrv, rdev, y, u, v );
          default:
               color3d = PIXEL_ARGB( color.a, color.r,
                                     color.g, color.b );
               break;
     }
     
     radeon_waitfifo( rdrv, rdev, 1 );
     radeon_out32( rdrv->mmio_base, R200_PP_TFACTOR_0, color3d );
     
     RADEON_SET( COLOR );
}

void r200_set_src_colorkey( RadeonDriverData *rdrv,
                            RadeonDeviceData *rdev,
                            CardState        *state )
{
     volatile u8 *mmio = rdrv->mmio_base;
     
     if (RADEON_IS_SET( SRC_COLORKEY ))
          return;
     
     radeon_waitfifo( rdrv, rdev, 2 );
     radeon_out32( mmio, CLR_CMP_CLR_SRC, state->src_colorkey ); 
     radeon_out32( mmio, CLR_CMP_MASK, rdev->src_mask );    
     
     RADEON_SET( SRC_COLORKEY );
}

void r200_set_blend_function( RadeonDriverData *rdrv,
                              RadeonDeviceData *rdev,
                              CardState        *state )
{
     volatile u8   *mmio   = rdrv->mmio_base;
     u32            sblend;
     u32            dblend;
     
     if (RADEON_IS_SET( SRC_BLEND ) && RADEON_IS_SET( DST_BLEND ))
          return;

     sblend = r200SrcBlend[state->src_blend-1];
     dblend = r200DstBlend[state->dst_blend-1];

     if (!DFB_PIXELFORMAT_HAS_ALPHA(rdev->dst_format)) {
          if (sblend == SRC_BLEND_GL_DST_ALPHA)
               sblend = SRC_BLEND_GL_ONE;
          else if (sblend == SRC_BLEND_GL_ONE_MINUS_DST_ALPHA)
               sblend = SRC_BLEND_GL_ZERO;
                    
          if (dblend == DST_BLEND_GL_DST_ALPHA)
               dblend = DST_BLEND_GL_ONE;
          else if (dblend == DST_BLEND_GL_ONE_MINUS_DST_ALPHA)
               dblend = DST_BLEND_GL_ZERO;
     }
     else if (rdev->dst_format == DSPF_A8) {
          if (sblend == SRC_BLEND_GL_DST_ALPHA)
               sblend = SRC_BLEND_GL_DST_COLOR;
          else if (sblend == SRC_BLEND_GL_ONE_MINUS_DST_ALPHA)
               sblend = SRC_BLEND_GL_ONE_MINUS_DST_COLOR;

          if (dblend == DST_BLEND_GL_DST_ALPHA)
               dblend = DST_BLEND_GL_DST_COLOR;
          else if (dblend == DST_BLEND_GL_ONE_MINUS_DST_ALPHA)
               dblend = DST_BLEND_GL_ONE_MINUS_DST_COLOR;
     }
     
     radeon_waitfifo( rdrv, rdev, 1 ); 
     radeon_out32( mmio, RB3D_BLENDCNTL, sblend | dblend );
     
     RADEON_SET( SRC_BLEND );
     RADEON_SET( DST_BLEND );
}

void r200_set_render_options( RadeonDriverData *rdrv,
                              RadeonDeviceData *rdev,
                              CardState        *state )
{
     if (RADEON_IS_SET( RENDER_OPTIONS ))
          return;
          
     if (state->render_options & DSRO_MATRIX &&
        (!state->affine_matrix ||
          state->matrix[0] != (1<<16) || state->matrix[1] != 0 || state->matrix[2] != 0 ||
          state->matrix[3] != 0 || state->matrix[4] != (1<<16) || state->matrix[5] != 0)) {
          rdev->matrix = state->matrix;
          rdev->affine_matrix = state->affine_matrix;
     }
     else {
          rdev->matrix = NULL;
     }
     
     if ((rdev->render_options & DSRO_ANTIALIAS) != (state->render_options & DSRO_ANTIALIAS)) {
          RADEON_UNSET( DRAWING_FLAGS );
          RADEON_UNSET( BLITTING_FLAGS );
     }     
     rdev->render_options = state->render_options;
     
     RADEON_SET( RENDER_OPTIONS );
}

/* NOTES:
 * - We use texture unit 0 for blitting functions,
 *          texture unit 1 for drawing functions
 * - Default blend equation is ADD_CLAMP (A * B + C)
 */

void r200_set_drawingflags( RadeonDriverData *rdrv,
                            RadeonDeviceData *rdev,
                            CardState        *state )
{
     volatile u8   *mmio        = rdrv->mmio_base;
     u32            master_cntl = rdev->gui_master_cntl       |
                                  GMC_SRC_DATATYPE_MONO_FG_LA | 
                                  GMC_BRUSH_SOLID_COLOR       |
                                  GMC_CLR_CMP_CNTL_DIS;
     u32            rb3d_cntl   = rdev->rb3d_cntl & ~DITHER_ENABLE;
     u32            pp_cntl     = TEX_BLEND_1_ENABLE;
     u32            cblend      = R200_TXC_ARG_C_TFACTOR_COLOR;
     
     if (RADEON_IS_SET( DRAWING_FLAGS ))
          return;

     if (rdev->dst_422) {
          pp_cntl |= TEX_1_ENABLE;
          cblend   = R200_TXC_ARG_C_R1_COLOR;
     }
     else if (rdev->dst_format == DSPF_A8) {
          cblend = R200_TXC_ARG_C_TFACTOR_ALPHA;
     }
     
     if (state->drawingflags & DSDRAW_BLEND)
          rb3d_cntl |= ALPHA_BLEND_ENABLE;
     
     if (state->drawingflags & DSDRAW_XOR) {
          rb3d_cntl   |= ROP_ENABLE;
          master_cntl |= GMC_ROP3_PATXOR;
     }
     else {
          master_cntl |= GMC_ROP3_PATCOPY;
     }
     
     if (state->render_options & DSRO_ANTIALIAS)
          pp_cntl |= ANTI_ALIAS_LINE_POLY;
     
     radeon_waitfifo( rdrv, rdev, 11 );
     radeon_out32( mmio, DP_GUI_MASTER_CNTL, master_cntl );
     radeon_out32( mmio, DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM );
     radeon_out32( mmio, RB3D_CNTL, rb3d_cntl );
     radeon_out32( mmio, SE_CNTL, DIFFUSE_SHADE_FLAT  |
                                  ALPHA_SHADE_FLAT    |
                                  BFACE_SOLID         |
                                  FFACE_SOLID         |
                                  VTX_PIX_CENTER_OGL  |
                                  ROUND_MODE_ROUND    |
				              ROUND_PREC_4TH_PIX );
     radeon_out32( mmio, PP_CNTL, pp_cntl );
     radeon_out32( mmio, R200_PP_TXCBLEND_1, cblend );
     radeon_out32( mmio, R200_PP_TXCBLEND2_1, (1 << R200_TXC_TFACTOR_SEL_SHIFT) |
                                              R200_TXC_OUTPUT_REG_R0            |
                                              R200_TXC_CLAMP_0_1 );
     radeon_out32( mmio, R200_PP_TXABLEND_1, R200_TXA_ARG_C_TFACTOR_ALPHA );
     radeon_out32( mmio, R200_PP_TXABLEND2_1, (1 << R200_TXA_TFACTOR_SEL_SHIFT) |
                                              R200_TXA_OUTPUT_REG_R0            |
                                              R200_TXA_CLAMP_0_1 );
     radeon_out32( mmio, R200_SE_VTX_FMT_0, R200_VTX_XY );
     radeon_out32( mmio, R200_SE_VTX_FMT_1, 0 );
     
     rdev->drawingflags = state->drawingflags;

     RADEON_SET  ( DRAWING_FLAGS );
     RADEON_UNSET( BLITTING_FLAGS );
}

void r200_set_blittingflags( RadeonDriverData *rdrv,
                             RadeonDeviceData *rdev,
                             CardState        *state )
{
     volatile u8   *mmio        = rdrv->mmio_base;
     u32            master_cntl = rdev->gui_master_cntl |
                                  GMC_BRUSH_NONE        |
                                  GMC_SRC_DATATYPE_COLOR;
     u32            cmp_cntl    = 0;
     u32            rb3d_cntl   = rdev->rb3d_cntl;
     u32            se_cntl     = BFACE_SOLID        |
                                  FFACE_SOLID        |
                                  VTX_PIX_CENTER_OGL |
                                  ROUND_MODE_ROUND;
     u32            pp_cntl     = TEX_0_ENABLE;
     u32            cblend      = R200_TXC_ARG_C_R0_COLOR;
     u32            ablend      = R200_TXA_ARG_C_R0_ALPHA;
     u32            vtx_fmt     = R200_VTX_XY;
     u32            vte_cntl;
     
     if (RADEON_IS_SET( BLITTING_FLAGS ))
          return;
 
     if (rdev->accel == DFXL_TEXTRIANGLES) {
          se_cntl  |= DIFFUSE_SHADE_GOURAUD  |
                      ALPHA_SHADE_GOURAUD    |
                      SPECULAR_SHADE_GOURAUD |
                      FLAT_SHADE_VTX_LAST    |
                      ROUND_PREC_8TH_PIX;
          vtx_fmt  |= R200_VTX_Z0 | R200_VTX_W0;
          vte_cntl  = 0;
     }
     else {
          se_cntl  |= DIFFUSE_SHADE_FLAT |
                      ALPHA_SHADE_FLAT   |
                      ROUND_PREC_4TH_PIX; 
          vte_cntl  = R200_VTX_ST_DENORMALIZED;
     }
    
     if (state->blittingflags & (DSBLIT_BLEND_COLORALPHA |
                                 DSBLIT_BLEND_ALPHACHANNEL)) {
          if (state->blittingflags & DSBLIT_BLEND_COLORALPHA) {
               if (state->blittingflags & DSBLIT_BLEND_ALPHACHANNEL)
                    ablend = R200_TXA_ARG_A_R0_ALPHA | R200_TXA_ARG_B_TFACTOR_ALPHA;
               else
                    ablend = R200_TXA_ARG_C_TFACTOR_ALPHA;

               pp_cntl |= TEX_BLEND_0_ENABLE;
          }
          
          rb3d_cntl |= ALPHA_BLEND_ENABLE;
     }

     if (rdev->dst_format != DSPF_A8) {
          if (state->blittingflags & (DSBLIT_SRC_MASK_ALPHA | DSBLIT_SRC_MASK_COLOR)) {
               if (state->blittingflags & DSBLIT_SRC_MASK_ALPHA)
                    ablend = R200_TXA_ARG_A_R0_ALPHA | R200_TXA_ARG_B_R1_ALPHA;
               
               if (state->blittingflags & DSBLIT_SRC_MASK_COLOR)
                    cblend = R200_TXC_ARG_A_R0_COLOR | R200_TXC_ARG_B_R1_COLOR;
                    
               pp_cntl |= TEX_1_ENABLE | TEX_BLEND_0_ENABLE;
          }
          else if (state->blittingflags & DSBLIT_COLORIZE) {
               if (rdev->dst_422) {
                    cblend = (rdev->src_format == DSPF_A8)
                             ? (R200_TXC_ARG_C_R1_COLOR)
                             : (R200_TXC_ARG_A_R0_COLOR | R200_TXC_ARG_B_R1_COLOR);

                    pp_cntl |= TEX_1_ENABLE;
               }
               else {
                    cblend = (rdev->src_format == DSPF_A8)
                             ? (R200_TXC_ARG_C_TFACTOR_COLOR)
                             : (R200_TXC_ARG_A_R0_COLOR | R200_TXC_ARG_B_TFACTOR_COLOR);
               }
          
               pp_cntl |= TEX_BLEND_0_ENABLE;
          }
          else if (state->blittingflags & DSBLIT_SRC_PREMULTCOLOR) {
               cblend = (rdev->src_format == DSPF_A8)
                        ? (R200_TXC_ARG_C_R0_ALPHA)
                        : (R200_TXC_ARG_A_R0_COLOR | R200_TXC_ARG_B_TFACTOR_ALPHA);
            
               pp_cntl |= TEX_BLEND_0_ENABLE;
          }
          else if (state->blittingflags & DSBLIT_SRC_PREMULTIPLY) {
               cblend = (rdev->src_format == DSPF_A8)
                        ? (R200_TXC_ARG_C_R0_ALPHA)
                        : (R200_TXC_ARG_A_R0_COLOR | R200_TXC_ARG_B_R0_ALPHA);
            
               pp_cntl |= TEX_BLEND_0_ENABLE;
          }
     } /* DSPF_A8 */
     else {
          if (state->blittingflags & DSBLIT_SRC_MASK_ALPHA) {
               ablend = R200_TXA_ARG_A_R0_ALPHA | R200_TXA_ARG_B_R1_ALPHA;
               cblend = R200_TXC_ARG_A_R0_ALPHA | R200_TXC_ARG_B_R1_ALPHA;
               pp_cntl |= TEX_1_ENABLE;
          }
          else if (state->blittingflags & DSBLIT_BLEND_COLORALPHA) {
               if (state->blittingflags & DSBLIT_BLEND_ALPHACHANNEL)
                    cblend = R200_TXC_ARG_A_R0_ALPHA | R200_TXC_ARG_B_TFACTOR_ALPHA;
               else
                    cblend = R200_TXC_ARG_C_TFACTOR_ALPHA;
          }
          else {
               cblend = R200_TXC_ARG_C_R0_ALPHA;
          }

          pp_cntl |= TEX_BLEND_0_ENABLE;
     }
 
     if (state->blittingflags & DSBLIT_SRC_COLORKEY)
          cmp_cntl = SRC_CMP_EQ_COLOR | CLR_CMP_SRC_SOURCE;
     else
          master_cntl |= GMC_CLR_CMP_CNTL_DIS;

     if (state->blittingflags & DSBLIT_XOR) {
          master_cntl |= GMC_ROP3_XOR;
          rb3d_cntl   |= ROP_ENABLE; 
     } 
     else {
          master_cntl |= GMC_ROP3_SRCCOPY;
     }
     
     if (state->render_options & DSRO_ANTIALIAS)
          pp_cntl |= ANTI_ALIAS_POLY;

     radeon_waitfifo( rdrv, rdev, 12 );
     radeon_out32( mmio, CLR_CMP_CNTL, cmp_cntl );
     radeon_out32( mmio, DP_GUI_MASTER_CNTL, master_cntl );
     radeon_out32( mmio, RB3D_CNTL, rb3d_cntl );
     radeon_out32( mmio, SE_CNTL, se_cntl );
     radeon_out32( mmio, PP_CNTL, pp_cntl );
     radeon_out32( mmio, R200_PP_TXCBLEND_0, cblend );
     radeon_out32( mmio, R200_PP_TXCBLEND2_0, R200_TXC_OUTPUT_REG_R0 |
                                              R200_TXC_CLAMP_0_1 );
     radeon_out32( mmio, R200_PP_TXABLEND_0, ablend );
     radeon_out32( mmio, R200_PP_TXABLEND2_0, R200_TXA_OUTPUT_REG_R0 |
                                              R200_TXA_CLAMP_0_1 );
     radeon_out32( mmio, R200_SE_VTX_FMT_0, vtx_fmt );
     radeon_out32( mmio, R200_SE_VTX_FMT_1, 2 << R200_VTX_TEX0_COMP_CNT_SHIFT );
     radeon_out32( mmio, R200_SE_VTE_CNTL, vte_cntl );
     
     rdev->blittingflags = state->blittingflags;
     
     RADEON_SET  ( BLITTING_FLAGS );
     RADEON_UNSET( DRAWING_FLAGS );
}

