/*
   (c) Copyright 2001-2009  The world wide DirectFB Open Source Community (directfb.org)
   (c) Copyright 2000-2004  Convergence (integrated media) GmbH

   All rights reserved.

   Written by Denis Oliver Kropp <dok@directfb.org>,
              Andreas Hundt <andi@fischlustig.de>,
              Sven Neumann <neo@directfb.org>,
              Ville Syrjälä <syrjala@sci.fi> and
              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/system.h>
#include <core/gfxcard.h>
#include <core/surface.h>

#include <gfx/convert.h>
#include <gfx/util.h>

#include "nvidia.h"
#include "nvidia_regs.h"
#include "nvidia_accel.h"
#include "nvidia_objects.h"
#include "nvidia_state.h"


#define NVIDIA_IS_SET( flag )  ((nvdev->set & SMF_##flag) == SMF_##flag)

#define NVIDIA_SET( flag )      nvdev->set |= SMF_##flag

#define NVIDIA_UNSET( flag )    nvdev->set &= ~SMF_##flag



void nv_set_destination( NVidiaDriverData *nvdrv,
                         NVidiaDeviceData *nvdev,
                         CardState        *state )
{
     CoreSurface   *surface = state->destination;
     volatile u8   *mmio    = nvdrv->mmio_base;
     u32            dst_offset;
     u32            dst_pitch;
     u32            src_pitch;
     
     if (NVIDIA_IS_SET( DESTINATION ))
          return;
          
     dst_offset = (state->dst.offset + nvdev->fb_offset) & ~63;
     dst_pitch  = state->dst.pitch & ~31;
     src_pitch  = (nvdev->src_pitch & ~31) ? : 32; // align to 32, maybe system buffer pitch

     if (nvdev->dst_format != surface->config.format) {
          u32   sformat2D = 0;
          u32   sformat3D = 0;
          u32   cformat   = 0;
          bool  dst_422 = false;

          switch (surface->config.format) {
               case DSPF_A8:
               case DSPF_LUT8:
               case DSPF_ALUT44:
               case DSPF_RGB332:
                    sformat2D = SURFACES2D_FORMAT_Y8;
                    cformat   = RECT_COLOR_FORMAT_Y32;
                    break;
               case DSPF_RGB555:
                    sformat2D = SURFACES2D_FORMAT_X1R5G5B5;
                    sformat3D = SURFACES3D_FORMAT_COLOR_X1R5G5B5;
                    cformat   = RECT_COLOR_FORMAT_A1Y15;
                    break;
               case DSPF_ARGB1555:
                    sformat2D = SURFACES2D_FORMAT_A1R5G5B5;
                    sformat3D = SURFACES3D_FORMAT_COLOR_A1R5G5B5;
                    cformat   = RECT_COLOR_FORMAT_A1Y15;
                    break;
               case DSPF_RGB16:
                    sformat2D = SURFACES2D_FORMAT_R5G6B5;
                    sformat3D = SURFACES3D_FORMAT_COLOR_R5G6B5;
                    cformat   = RECT_COLOR_FORMAT_Y16;
                    break;
               case DSPF_RGB32:
                    sformat2D = SURFACES2D_FORMAT_X8R8G8B8;
                    sformat3D = SURAFCES3D_FORMAT_COLOR_X8R8G8B8;
                    cformat   = RECT_COLOR_FORMAT_Y32;
                    break;
               case DSPF_ARGB:
                    sformat2D = SURFACES2D_FORMAT_A8R8G8B8;
                    sformat3D = SURFACES3D_FORMAT_COLOR_A8R8G8B8;
                    cformat   = 0x0D;
                    break;
               case DSPF_YUY2:
                    sformat2D = SURFACES2D_FORMAT_A8R8G8B8;
                    cformat   = 0x12;
                    dst_422 = true;
                    break;
               case DSPF_UYVY:
                    sformat2D = SURFACES2D_FORMAT_A8R8G8B8;
                    cformat   = 0x13;
                    dst_422 = true;
                    break;
               default:
                    D_BUG( "unexpected pixelformat" );
                    return;
          }

          if (sformat2D == SURFACES2D_FORMAT_A8R8G8B8) {     
               /* need to set color format manually */
               nv_waitidle( nvdrv, nvdev );

               nv_out32( mmio, PRAMIN + (ADDR_RECTANGLE << 4) + 4, cformat << 8 );
               nv_out32( mmio, PRAMIN + (ADDR_TRIANGLE  << 4) + 4, cformat << 8 );
               nv_out32( mmio, PRAMIN + (ADDR_LINE      << 4) + 4, cformat << 8 );

               nv_assign_object( nvdrv, nvdev, 
                                 SUBC_RECTANGLE, OBJ_RECTANGLE, true );
               nv_assign_object( nvdrv, nvdev,
                                 SUBC_TRIANGLE, OBJ_TRIANGLE, true );
               nv_assign_object( nvdrv, nvdev,
                                 SUBC_LINE, OBJ_LINE, true );
          } else {
               nv_begin( SUBC_RECTANGLE, RECT_COLOR_FORMAT, 1 );
               nv_outr( cformat );
               
               nv_begin( SUBC_TRIANGLE, TRI_COLOR_FORMAT, 1 );
               nv_outr( cformat );
               
               nv_begin( SUBC_LINE, LINE_COLOR_FORMAT, 1 );
               nv_outr( cformat );
          }

          nv_assign_object( nvdrv, nvdev, 
                            SUBC_SURFACES2D, OBJ_SURFACES2D, false );

          nv_begin( SUBC_SURFACES2D, SURFACES2D_FORMAT, 2 );
          nv_outr( sformat2D );
          nv_outr( (dst_pitch << 16) | (src_pitch & 0xFFFF) );
          nv_begin( SUBC_SURFACES2D, SURFACES2D_DST_OFFSET, 1 );
          nv_outr( dst_offset );

          if (nvdev->enabled_3d && sformat3D) {
               nv_assign_object( nvdrv, nvdev, 
                                 SUBC_SURFACES3D, OBJ_SURFACES3D, false );
               
               nv_begin( SUBC_SURFACES3D, SURFACES3D_FORMAT, 1 );
               nv_outr( sformat3D | SURFACES3D_FORMAT_TYPE_PITCH );
               nv_begin( SUBC_SURFACES3D, SURFACES3D_PITCH, 2 );
               nv_outr( (64 << 16) | (dst_pitch & 0xFFFF) );
               nv_outr( dst_offset );
          }
          
          if (nvdev->dst_422 != dst_422) {
               NVIDIA_UNSET( CLIP );
               NVIDIA_UNSET( BLITTING_FLAGS );
               nvdev->dst_422 = dst_422;
          }
          
          NVIDIA_UNSET( COLOR );
          NVIDIA_UNSET( DST_BLEND );
     }
     else if (nvdev->dst_offset != dst_offset ||
              nvdev->dst_pitch  != dst_pitch) {
          nv_assign_object( nvdrv, nvdev,
                            SUBC_SURFACES2D, OBJ_SURFACES2D, false );

          nv_begin( SUBC_SURFACES2D, SURFACES2D_PITCH, 1 );
          nv_outr( (dst_pitch << 16) | (src_pitch & 0xFFFF) );
          nv_begin( SUBC_SURFACES2D, SURFACES2D_DST_OFFSET, 1 );
          nv_outr( dst_offset );

          if (nvdev->enabled_3d) {
               nv_assign_object( nvdrv, nvdev,
                                 SUBC_SURFACES3D, OBJ_SURFACES3D, false );
               
               nv_begin( SUBC_SURFACES3D, SURFACES3D_PITCH, 2 );
               nv_outr( (64 << 16) | (dst_pitch & 0xFFFF) );
               nv_outr( dst_offset );
          }
     }
     
     nvdev->dst_format = surface->config.format;
     nvdev->dst_offset = dst_offset;
     nvdev->dst_pitch  = dst_pitch;
     
     NVIDIA_SET( DESTINATION );
}

void nv_set_source( NVidiaDriverData *nvdrv,
                    NVidiaDeviceData *nvdev,
                    CardState        *state )
{
     CoreSurface *surface = state->source;
    
     nvdev->src_lock = &state->src;
    
     if (NVIDIA_IS_SET( SOURCE )) {
          if ((state->blittingflags & DSBLIT_DEINTERLACE) ==
              (nvdev->blittingflags & DSBLIT_DEINTERLACE))
               return;
     }
          
     if (state->src.buffer->policy == CSP_SYSTEMONLY) { 
          if (!nvdev->src_system) {
               nv_assign_object( nvdrv, nvdev,
                                 SUBC_IMAGEBLT, OBJ_IMAGEBLT, false );
               nv_assign_object( nvdrv, nvdev,
                                 SUBC_STRETCHEDIMAGE, OBJ_STRETCHEDIMAGE, false );
               
               NVIDIA_UNSET( BLITTING_FLAGS );
          }

          nvdev->src_address = state->src.addr;
          nvdev->src_pitch   = state->src.pitch;
          nvdev->src_system  = true;
     }
     else {
          u32 src_offset = (state->src.offset + nvdev->fb_offset) & ~63;
          u32 src_pitch  = state->src.pitch & ~31;

          if (nvdev->src_offset != src_offset ||
              nvdev->src_pitch  != src_pitch) {
               nv_assign_object( nvdrv, nvdev,
                                 SUBC_SURFACES2D, OBJ_SURFACES2D, false );
          
               nv_begin( SUBC_SURFACES2D, SURFACES2D_PITCH, 2 );
               nv_outr( (nvdev->dst_pitch << 16) | (src_pitch & 0xFFFF) );
               nv_outr( src_offset );
          }

          if (nvdev->src_system) {
               nv_assign_object( nvdrv, nvdev,
                                 SUBC_SCREENBLT, OBJ_SCREENBLT, false );
               nv_assign_object( nvdrv, nvdev,
                                 SUBC_SCALEDIMAGE, OBJ_SCALEDIMAGE, false );
                          
               NVIDIA_UNSET( BLITTING_FLAGS );
          }
               
          nvdev->src_offset = src_offset;
          nvdev->src_pitch  = src_pitch;
          nvdev->src_system = false;
     }

     nvdev->src_width  = surface->config.size.w;
     nvdev->src_height = surface->config.size.h;

     if (state->blittingflags & DSBLIT_DEINTERLACE) {
          nvdev->src_height /= 2;
          if (surface->config.caps & DSCAPS_SEPARATED) {
               if (surface->field) {
                    nvdev->src_address += nvdev->src_height * nvdev->src_pitch;
                    nvdev->src_offset  += nvdev->src_height * nvdev->src_pitch;
               }
          } else {
               if (surface->field) {
                    nvdev->src_address += nvdev->src_pitch;
                    nvdev->src_offset  += nvdev->src_pitch;
               }
               nvdev->src_pitch *= 2;
          }
          nvdev->src_interlaced = true;
     } else
          nvdev->src_interlaced = false;
     
     if (nvdev->enabled_3d) {
          u32 size_u = direct_log2(surface->config.size.w) & 0xF;
          u32 size_v = direct_log2(surface->config.size.h) & 0xF;

          nvdev->state3d[1].offset  = nvdev->fb_offset + nvdev->buf_offset[1];
          nvdev->state3d[1].format &= 0xFF00FFFF;
          nvdev->state3d[1].format |= (size_u << 16) | (size_v << 20);
     }

     if (nvdev->src_format != surface->config.format) {
          NVIDIA_UNSET( SRC_BLEND );
          NVIDIA_UNSET( BLITTING_FLAGS );
          nvdev->src_format = surface->config.format;
     }
     
     NVIDIA_SET( SOURCE );
}

void nv_set_clip( NVidiaDriverData *nvdrv,
                  NVidiaDeviceData *nvdev,
                  CardState        *state )
{
     DFBRectangle *cr = &nvdev->clip;
     
     if (NVIDIA_IS_SET( CLIP ))
          return;

     cr->x = state->clip.x1;
     cr->y = state->clip.y1;
     cr->w = state->clip.x2 - state->clip.x1 + 1;
     cr->h = state->clip.y2 - state->clip.y1 + 1;

     if (nvdev->dst_422) {
          cr->x =  cr->x / 2;
          cr->w = (cr->w / 2) ? : 1;
     }

     nv_begin( SUBC_CLIP, CLIP_TOP_LEFT, 2 );
     nv_outr( (cr->y << 16) | (cr->x & 0xFFFF) );
     nv_outr( (cr->h << 16) | (cr->w & 0xFFFF) );

     NVIDIA_SET( CLIP );
}

void nv_set_drawing_color( NVidiaDriverData *nvdrv,
                           NVidiaDeviceData *nvdev,
                           CardState        *state )
{
     DFBColor color   = state->color;
     int      y, u, v;
     
     if (NVIDIA_IS_SET( DRAWING_COLOR ) && NVIDIA_IS_SET( DRAWING_FLAGS ))
          return;
     
     switch (nvdev->dst_format) {
          case DSPF_A8:
               nvdev->color2d = color.a;
               break;
          case DSPF_LUT8:
               nvdev->color2d = state->color_index;
               break;
          case DSPF_ALUT44:
               nvdev->color2d = (state->color_index & 0x0F) |
                                (state->color.a     & 0xF0);
               break;
          case DSPF_RGB332:
               nvdev->color2d = PIXEL_RGB332( color.r,
                                              color.g,
                                              color.b );
               break;
          case DSPF_RGB555:
          case DSPF_ARGB1555:
               nvdev->color2d = PIXEL_ARGB1555( color.a,
                                                color.r,
                                                color.g,
                                                color.b );
               break;
          case DSPF_RGB16:
               nvdev->color2d = PIXEL_RGB16( color.r,
                                             color.g,
                                             color.b );
               break;
          case DSPF_RGB32:
               nvdev->color2d = PIXEL_RGB32( color.r,
                                             color.g,
                                             color.b );
               break;
          case DSPF_ARGB:
               nvdev->color2d = PIXEL_ARGB( color.a,
                                            color.r,
                                            color.g,
                                            color.b );
               break;

          case DSPF_YUY2:
               RGB_TO_YCBCR( color.r, color.g, color.b, y, u, v );
               nvdev->color2d = PIXEL_YUY2( y, u, v );
               break;

          case DSPF_UYVY:
               RGB_TO_YCBCR( color.r, color.g, color.b, y, u, v );
               nvdev->color2d = PIXEL_UYVY( y, u, v );
               break;

          default:
               D_BUG( "unexpected pixelformat" );
               break;
     }

     nvdev->color3d = PIXEL_ARGB(color.a, color.r, color.g, color.b);
     
     if (nvdev->dst_format == DSPF_ARGB1555) {
          nv_assign_object( nvdrv, nvdev,
                            SUBC_SURFACES2D, OBJ_SURFACES2D, false );
          
          nv_begin( SUBC_SURFACES2D, SURFACES2D_FORMAT, 1 );
          nv_outr( (nvdev->color2d & 0x8000) 
                   ? SURFACES2D_FORMAT_A1R5G5B5
                   : SURFACES2D_FORMAT_X1R5G5B5 );
     }
  
     if (state->drawingflags & DSDRAW_BLEND && !nvdev->enabled_3d) {
          if (!nvdev->beta1_set || nvdev->beta1_val != (color.a << 23)) {
               nv_assign_object( nvdrv, nvdev, SUBC_BETA1, OBJ_BETA1, false );
                    
               nv_begin( SUBC_BETA1, BETA1_FACTOR, 1 );
               nv_outr( color.a << 23 );

               nvdev->beta1_val = color.a << 23;
               nvdev->beta1_set = true;
          }
     }
     
     NVIDIA_SET  ( DRAWING_COLOR );
     NVIDIA_UNSET( BLITTING_COLOR );
}

void nv_set_blitting_color( NVidiaDriverData *nvdrv,
                            NVidiaDeviceData *nvdev,
                            CardState        *state )
{
     DFBColor color = state->color;
     
     if (NVIDIA_IS_SET( BLITTING_COLOR ) && NVIDIA_IS_SET( BLITTING_FLAGS ))
          return;
                    
     if (state->blittingflags & (DSBLIT_COLORIZE | DSBLIT_SRC_PREMULTCOLOR) ||
        (state->blittingflags & DSBLIT_BLEND_ALPHACHANNEL && state->src_blend == DSBF_ONE)) {       
          nvdev->color3d = (state->blittingflags & DSBLIT_BLEND_COLORALPHA)
                           ? (color.a << 24) : 0xFF000000;
          
          if (state->blittingflags & DSBLIT_COLORIZE &&
              state->blittingflags & (DSBLIT_SRC_PREMULTCOLOR | DSBLIT_BLEND_COLORALPHA)) {
               nvdev->color3d |= PIXEL_RGB32( color.r * color.a / 0xFF,
                                              color.g * color.a / 0xFF,
                                              color.b * color.a / 0xFF );
          }
          else if (state->blittingflags & DSBLIT_COLORIZE) {
               nvdev->color3d |= PIXEL_RGB32( color.r, color.g, color.b );
          }
          else if (state->blittingflags & (DSBLIT_SRC_PREMULTCOLOR | DSBLIT_BLEND_COLORALPHA)) {
               nvdev->color3d |= PIXEL_RGB32( color.a, color.a, color.a );
          }
          else {
               nvdev->color3d |= 0x00FFFFFF;
          }

          if (!nvdev->beta4_set || nvdev->beta4_val != nvdev->color3d) {
               nv_assign_object( nvdrv, nvdev,
                                 SUBC_BETA4, OBJ_BETA4, false );
                                 
               nv_begin( SUBC_BETA4, BETA4_FACTOR, 1 );
               nv_outr( nvdev->color3d );

               nvdev->beta4_val = nvdev->color3d;
               nvdev->beta4_set = true;
          }
     }
     else if (state->blittingflags & (DSBLIT_BLEND_COLORALPHA |
                                      DSBLIT_BLEND_ALPHACHANNEL)) {
          u32 beta1;
          
          if (state->blittingflags & DSBLIT_BLEND_COLORALPHA) {
               nvdev->color3d = (color.a << 24) | 0x00FFFFFF;
               beta1          =  color.a << 23;
          } else {
               nvdev->color3d = 0xFFFFFFFF;
               beta1          = 0x7F800000;
          }
          
          if (!nvdev->beta1_set || nvdev->beta1_val != beta1) {
               nv_assign_object( nvdrv, nvdev,
                                 SUBC_BETA1, OBJ_BETA1, false );

               nv_begin( SUBC_BETA1, BETA1_FACTOR, 1 );
               nv_outr( beta1 );

               nvdev->beta1_val = beta1;
               nvdev->beta1_set = true;
          }
     }
     
     NVIDIA_SET  ( BLITTING_COLOR );
     NVIDIA_UNSET( DRAWING_COLOR );
}

void nv_set_blend_function( NVidiaDriverData *nvdrv,
                            NVidiaDeviceData *nvdev,
                            CardState        *state )
{
     DFBSurfaceBlendFunction sblend, dblend;
     
     if (NVIDIA_IS_SET( SRC_BLEND ) && NVIDIA_IS_SET( DST_BLEND ))
          return;
       
     sblend = state->src_blend;
     dblend = state->dst_blend;
     
     if (!DFB_PIXELFORMAT_HAS_ALPHA(nvdev->dst_format)) {
          if (sblend == DSBF_DESTALPHA)
               sblend = DSBF_ONE;
          else if (sblend == DSBF_INVDESTALPHA)
               sblend = DSBF_ZERO;
               
          if (dblend == DSBF_DESTALPHA)
               dblend = DSBF_ONE;
          else if (dblend == DSBF_INVDESTALPHA)
               dblend = DSBF_ZERO;
     }
          
     nvdev->state3d[0].blend &= 0x00FFFFFF;
     nvdev->state3d[0].blend |= (sblend << 24) | (dblend << 28);
     nvdev->state3d[1].blend &= 0x00FFFFFF;
     nvdev->state3d[1].blend |= (sblend << 24) | (dblend << 28);
         
     if (!NVIDIA_IS_SET( SRC_BLEND ))
          NVIDIA_UNSET( BLITTING_FLAGS );
     NVIDIA_SET( SRC_BLEND );
     NVIDIA_SET( DST_BLEND );
}

void nv_set_drawingflags( NVidiaDriverData *nvdrv,
                          NVidiaDeviceData *nvdev,
                          CardState        *state )
{
     if (NVIDIA_IS_SET( DRAWING_FLAGS ))
          return;
          
     if (!nvdev->enabled_3d) {
          u32 operation;
          
          if (state->drawingflags & DSDRAW_BLEND)
               operation = OPERATION_BLEND;
          else
               operation = OPERATION_SRCCOPY;
          
          if (nvdev->drawing_operation != operation) {
               nv_begin( SUBC_RECTANGLE, RECT_OPERATION, 1 );
               nv_outr( operation );
               
               nv_begin( SUBC_TRIANGLE, TRI_OPERATION, 1 );
               nv_outr( operation );
               
               nv_begin( SUBC_LINE, LINE_OPERATION, 1 );
               nv_outr( operation );
               
               nvdev->drawing_operation = operation;
          }
     }
     else {
          if (state->drawingflags & DSDRAW_BLEND)
               nvdev->state3d[0].blend |= TXTRI_BLEND_ALPHABLEND_ENABLE;
          else
               nvdev->state3d[0].blend &= ~TXTRI_BLEND_ALPHABLEND_ENABLE;
     }

     nvdev->drawingflags = state->drawingflags;
     
     NVIDIA_SET( DRAWING_FLAGS );
}

void nv_set_blittingflags( NVidiaDriverData *nvdrv,
                           NVidiaDeviceData *nvdev,
                           CardState        *state )
{
     u32  operation;
     bool src_alpha;
     
     if (NVIDIA_IS_SET( BLITTING_FLAGS ))
          return;
          
     operation = (nvdev->arch > NV_ARCH_04) ? OPERATION_SRCCOPY : OPERATION_COPY;
     src_alpha = true;
     
     if (state->blittingflags & (DSBLIT_BLEND_COLORALPHA |
                                 DSBLIT_BLEND_ALPHACHANNEL)) {
          if (state->blittingflags & (DSBLIT_COLORIZE | DSBLIT_SRC_PREMULTCOLOR) ||
             (state->blittingflags & DSBLIT_BLEND_ALPHACHANNEL && state->src_blend == DSBF_ONE)) {
               operation = OPERATION_BLEND_PREMULTIPLIED;
          }
          else {
               operation = OPERATION_BLEND;
               src_alpha = !!(state->blittingflags & DSBLIT_BLEND_ALPHACHANNEL);
          }
     }
     else if (state->blittingflags & (DSBLIT_COLORIZE | DSBLIT_SRC_PREMULTCOLOR)) {
          operation = OPERATION_COLOR_MULTIPLY;
     }

     if (nvdev->src_system) {
          switch (nvdev->src_format) {
               case DSPF_RGB555:
                    nvdev->system_format = IBLIT_COLOR_FORMAT_X1R5G5B5;
                    break;
               case DSPF_ARGB1555:
                    nvdev->system_format = src_alpha
                                           ? IBLIT_COLOR_FORMAT_A1R5G5B5 
                                           : IBLIT_COLOR_FORMAT_X1R5G5B5;
                    break;
               case DSPF_RGB16:
                    nvdev->system_format = IBLIT_COLOR_FORMAT_R5G6B5;
                    break;
               case DSPF_RGB32:
                    nvdev->system_format = IBLIT_COLOR_FORMAT_X8R8G8B8;
                    break;
               case DSPF_ARGB:
                    nvdev->system_format = src_alpha 
                                           ? IBLIT_COLOR_FORMAT_A8R8G8B8 
                                           : IBLIT_COLOR_FORMAT_X8R8G8B8;
                    break;
               default:
                    D_BUG( "unexpected pixelformat" );
                    break;
          }
 
          if (nvdev->system_operation != operation) {
               nv_begin( SUBC_IMAGEBLT, IBLIT_OPERATION, 1 );
               nv_outr( operation );
               
               nv_begin( SUBC_STRETCHEDIMAGE, ISTRETCH_OPERATION, 1 );
               nv_outr( operation );
               
               nvdev->system_operation = operation;
          }
     }
     else {
          switch (nvdev->src_format) {
               case DSPF_A8:
                    nvdev->scaler_format = SCALER_COLOR_FORMAT_AY8;
                    break;
               case DSPF_LUT8:
               case DSPF_ALUT44:
               case DSPF_RGB332:
                    nvdev->scaler_format = SCALER_COLOR_FORMAT_Y8;
                    break;
               case DSPF_RGB555:
                    nvdev->scaler_format = SCALER_COLOR_FORMAT_X1R5G5B5;
                    break;
               case DSPF_ARGB1555:
                    nvdev->scaler_format = src_alpha
                                           ? SCALER_COLOR_FORMAT_A1R5G5B5
                                           : SCALER_COLOR_FORMAT_X1R5G5B5;
                    break;
               case DSPF_RGB16:
                    nvdev->scaler_format = SCALER_COLOR_FORMAT_R5G6B5;
                    break;
               case DSPF_RGB32:
                    nvdev->scaler_format = SCALER_COLOR_FORMAT_X8R8G8B8;
                    break;
               case DSPF_ARGB:
                    nvdev->scaler_format = src_alpha
                                           ? SCALER_COLOR_FORMAT_A8R8G8B8
                                           : SCALER_COLOR_FORMAT_X8R8G8B8;
                    break;
               case DSPF_YUY2:
                    nvdev->scaler_format = nvdev->dst_422
                                           ? SCALER_COLOR_FORMAT_A8R8G8B8
                                           : SCALER_COLOR_FORMAT_V8YB8U8YA8;
                    break;
               case DSPF_UYVY:
                    nvdev->scaler_format = nvdev->dst_422
                                           ? SCALER_COLOR_FORMAT_A8R8G8B8
                                           : SCALER_COLOR_FORMAT_YB8V8YA8U8;
                    break;
               default:
                    D_BUG( "unexpected pixelformat 0x%08x", nvdev->src_format );
                    break;
          }
          
          if (nvdev->scaler_operation != operation) {
               nv_begin( SUBC_SCALEDIMAGE, SCALER_OPERATION, 1 );
               nv_outr( operation );
               
               nvdev->scaler_operation = operation;
          }
     }
     
     if (nvdev->enabled_3d) {
          nvdev->state3d[1].format &= 0xFFFFF0FF;
          nvdev->state3d[1].blend  &= 0xFF00FFF0;

          switch (nvdev->src_format) {
               case DSPF_RGB555:
                    nvdev->state3d[1].format |= TXTRI_FORMAT_COLOR_X1R5G5B5;
                    break;
               case DSPF_ARGB1555:
                    nvdev->state3d[1].format |= TXTRI_FORMAT_COLOR_A1R5G5B5;
                    break;
               case DSPF_A8:
               case DSPF_ARGB:
                    nvdev->state3d[1].format |= TXTRI_FORMAT_COLOR_A4R4G4B4;
                    break;
               default:
                    nvdev->state3d[1].format |= TXTRI_FORMAT_COLOR_R5G6B5;
                    break;
          }
          
          if (state->blittingflags & (DSBLIT_BLEND_COLORALPHA | DSBLIT_COLORIZE | 
                                      DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_SRC_PREMULTCOLOR)) {                         
               if (state->blittingflags & DSBLIT_BLEND_COLORALPHA)
                    nvdev->state3d[1].blend |= 
                         TXTRI_BLEND_TEXTUREMAPBLEND_MODULATEALPHA;
               else
                    nvdev->state3d[1].blend |= 
                         TXTRI_BLEND_TEXTUREMAPBLEND_MODULATE;

               if (state->blittingflags & (DSBLIT_BLEND_COLORALPHA |
                                           DSBLIT_BLEND_ALPHACHANNEL))
                    nvdev->state3d[1].blend |= TXTRI_BLEND_ALPHABLEND_ENABLE;
          } else
               nvdev->state3d[1].blend |= TXTRI_BLEND_TEXTUREMAPBLEND_COPY;
     }
 
     nvdev->blittingflags = state->blittingflags;
     
     NVIDIA_SET( BLITTING_FLAGS );
}

