/*
 * Xv driver for SiS 300, 315 and 330 series.
 *
 * Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1) Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2) Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3) The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Author:    Thomas Winischhofer <thomas@winischhofer.net>
 *
 * Formerly based on a mostly non-working code fragment for the 630 by
 * Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan which is
 * Copyright (C) 2000 Silicon Integrated Systems Corp, Inc.
 *
 * Basic structure based on the mga Xv driver by Mark Vojkovich
 * and i810 Xv driver by Jonathan Bian <jonathan.bian@intel.com>.
 *
 * All comments in this file are by Thomas Winischhofer.
 *
 * The overlay adaptor supports the following chipsets:
 *  SiS300: No registers >0x65, two overlays (one used for CRT1, one for CRT2)
 *  SiS630/730: No registers >0x6b, two overlays (one used for CRT1, one for CRT2)
 *  SiS550: Full register range, two overlays (one used for CRT1, one for CRT2)
 *  SiS315: Full register range, one overlay (used for both CRT1 and CRT2 alt.)
 *  SiS650/740: Full register range, one overlay (used for both CRT1 and CRT2 alt.)
 *  SiSM650/651: Full register range, two overlays (one used for CRT1, one for CRT2)
 *  SiS330: Full register range, one overlay (used for both CRT1 and CRT2 alt.)
 *  SiS661/741/760: Full register range, two overlays (one used for CRT1, one for CRT2)
 *  SiS340: - ? overlays. Extended registers for DDA.
 *  SiS761: - ? overlays. Extended registers for DDA.
 *  XGI Volari V3XT/V5/V8: 1 Overlay. Extended registers for DDA.
 *
 * Help for reading the code:
 * 315/550/650/740/M650/651/330/661/741/76x/340/XGI = SIS_315_VGA
 * 300/630/730                                      = SIS_300_VGA
 * For chipsets with 2 overlays, hasTwoOverlays will be true
 *
 * Notes on display modes:
 *
 * -) dual head mode:
 *    DISPMODE is either SINGLE1 or SINGLE2, hence you need to check dualHeadMode flag
 *    DISPMODE is _never_ MIRROR.
 *    a) Chipsets with 2 overlays:
 *       315/330 series: Only half sized overlays available (width 960), 660: 1536
 *       Overlay 1 is used on CRT1, overlay 2 for CRT2.
 *    b) Chipsets with 1 overlay:
 *       Full size overlays available.
 *       Overlay is used for either CRT1 or CRT2
 * -) merged fb mode:
 *    a) Chipsets with 2 overlays:
 *       315/330 series: Only half sized overlays available (width 960), 660: 1536
 *       DISPMODE is always MIRROR. Overlay 1 is used for CRT1, overlay 2 for CRT2.
 *    b) Chipsets with 1 overlay:
 *       Full size overlays available.
 *       DISPMODE is either SINGLE1 or SINGLE2. Overlay is used accordingly on either
 *       CRT1 or CRT2 (automatically, where it is located)
 * -) mirror mode (without dualhead or mergedfb)
 *    a) Chipsets with 2 overlays:
 *       315/330 series: Only half sized overlays available (width 960), 660: 1536
 *       DISPMODE is MIRROR. Overlay 1 is used for CRT1, overlay 2 for CRT2.
 *    b) Chipsets with 1 overlay:
 *       Full size overlays available.
 *       DISPMODE is either SINGLE1 or SINGLE2. Overlay is used depending on
 * 	 XvOnCRT2 flag.
 *
 * About the video blitter:
 * The video blitter adaptor supports 16 ports. By default, adaptor 0 will
 * be the overlay adaptor, adaptor 1 the video blitter. The option XvDefaultAdaptor
 * allows reversing this.
 * Since SiS does not provide information on the 3D engine, I could not
 * implement scaling. Instead, the driver paints a black border around the unscaled
 * video if the destination area is bigger than the video.
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "sis.h"
#ifdef SIS_USE_XAA
#include "xf86fbman.h"
#endif
#include "regionstr.h"

#include "xf86xv.h"
#include <X11/extensions/Xv.h>
#include "dixstruct.h"
#include "fourcc.h"

#define SIS_NEED_inSISREG
#define SIS_NEED_outSISREG
#define SIS_NEED_inSISIDXREG
#define SIS_NEED_outSISIDXREG
#define SIS_NEED_setSISIDXREGmask
#define SIS_NEED_MYMMIO
#include "sis_regs.h"

#ifdef INCL_YUV_BLIT_ADAPTOR
#include "sis310_accel.h"
#endif

#include "sis_video.h"

/*********************************
 *       Raw register access     *
 *********************************/

#if 0
static CARD32 _sisread(SISPtr pSiS, CARD32 reg)
{
    return *(pSiS->IOBase + reg);
}

static void _siswrite(SISPtr pSiS, CARD32 reg, CARD32 data)
{
    *(pSiS->IOBase + reg) = data;
}
#endif

static CARD8 getsrreg(SISPtr pSiS, CARD8 reg)
{
    CARD8 ret;
    inSISIDXREG(SISSR, reg, ret);
    return ret;
}

static CARD8 getvideoreg(SISPtr pSiS, CARD8 reg)
{
    CARD8 ret;
    inSISIDXREG(SISVID, reg, ret);
    return ret;
}

static __inline void setvideoreg(SISPtr pSiS, CARD8 reg, CARD8 data)
{
    outSISIDXREG(SISVID, reg, data);
}

static __inline void setvideoregmask(SISPtr pSiS, CARD8 reg, CARD8 data, CARD8 mask)
{
    setSISIDXREGmask(SISVID, reg, data, mask);
}

static void setsrregmask(SISPtr pSiS, CARD8 reg, CARD8 data, CARD8 mask)
{
    setSISIDXREGmask(SISSR, reg, data, mask);
}

/* VBlank */
static CARD8 vblank_active_CRT1(SISPtr pSiS, SISPortPrivPtr pPriv)
{
    return(inSISREG(SISINPSTAT) & 0x08); /* Verified */
}

static CARD8 vblank_active_CRT2(SISPtr pSiS, SISPortPrivPtr pPriv)
{
    CARD8 ret;

    if(pPriv->bridgeIsSlave) return(vblank_active_CRT1(pSiS, pPriv));

    if(pSiS->VGAEngine == SIS_315_VGA) {
       inSISIDXREG(SISPART1, 0x30, ret);
    } else {
       inSISIDXREG(SISPART1, 0x25, ret);
    }
    return(ret & 0x02);  /* Verified */
}

/* Scanline - unused */
#if 0
static CARD16 get_scanline_CRT1(SISPtr pSiS)
{
    CARD32 line;

    _siswrite(pSiS, REG_PRIM_CRT_COUNTER, 0x00000001);
    line = _sisread(pSiS, REG_PRIM_CRT_COUNTER);

    return((CARD16)((line >> 16) & 0x07FF));
}
#endif

static CARD16 get_scanline_CRT2(SISPtr pSiS, SISPortPrivPtr pPriv)
{
    CARD8 reg1, reg2;

    if(pSiS->VGAEngine == SIS_315_VGA) {
       inSISIDXREG(SISPART1, 0x32, reg1);
       inSISIDXREG(SISPART1, 0x33, reg2);
    } else {
       inSISIDXREG(SISPART1, 0x27, reg1);
       inSISIDXREG(SISPART1, 0x28, reg2);
    }

    return((CARD16)(reg1 | ((reg2 & 0x70) << 4)));
}

/* Helper: Count attributes */
static int
SiSCountAttributes(XF86AttributeRec *attrs)
{
   int num = 0;

   while(attrs[num].name) num++;

   return num;
}

/*********************************
 *          Video gamma          *
 *********************************/

static void
SiSComputeXvGamma(SISPtr pSiS)
{
    int num = 255, i;
    double red = 1.0 / (double)((double)pSiS->XvGammaRed / 1000);
    double green = 1.0 / (double)((double)pSiS->XvGammaGreen / 1000);
    double blue = 1.0 / (double)((double)pSiS->XvGammaBlue / 1000);

    for(i = 0; i <= num; i++) {
        pSiS->XvGammaRampRed[i] =
	    (red == 1.0) ? i : (CARD8)(pow((double)i / (double)num, red) * (double)num + 0.5);

	pSiS->XvGammaRampGreen[i] =
	    (green == 1.0) ? i : (CARD8)(pow((double)i / (double)num, green) * (double)num + 0.5);

	pSiS->XvGammaRampBlue[i] =
	    (blue == 1.0) ? i : (CARD8)(pow((double)i / (double)num, blue) * (double)num + 0.5);
    }
}

static void
SiSSetXvGamma(SISPtr pSiS)
{
    int i;
    UChar backup = getsrreg(pSiS, 0x1f);
    setsrregmask(pSiS, 0x1f, 0x08, 0x18);
    for(i = 0; i <= 255; i++) {
       SIS_MMIO_OUT32(pSiS->IOBase, 0x8570,
			(i << 24)			 |
			(pSiS->XvGammaRampBlue[i] << 16) |
			(pSiS->XvGammaRampGreen[i] << 8) |
			pSiS->XvGammaRampRed[i]);
    }
    setsrregmask(pSiS, 0x1f, backup, 0xff);
}

void
SiSUpdateXvGamma(SISPtr pSiS, SISPortPrivPtr pPriv)
{
    UChar sr7 = getsrreg(pSiS, 0x07);

    if(!pSiS->XvGamma) return;
    if(!(pSiS->MiscFlags & MISC_CRT1OVERLAYGAMMA)) return;

#ifdef SISDUALHEAD
    if((pPriv->dualHeadMode) && (!pSiS->SecondHead)) return;
#endif

    if(!(sr7 & 0x04)) return;

    SiSComputeXvGamma(pSiS);
    SiSSetXvGamma(pSiS);
}

static void
SISResetXvGamma(ScrnInfoPtr pScrn)
{
    SISPtr pSiS = SISPTR(pScrn);
    SISPortPrivPtr pPriv = GET_PORT_PRIVATE(pScrn);

    SiSUpdateXvGamma(pSiS, pPriv);
}

/*********************************
 *          InitVideo()          *
 *********************************/

void
SISInitVideo(ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    SISPtr pSiS = SISPTR(pScrn);
    XF86VideoAdaptorPtr *adaptors, *newAdaptors = NULL;
    XF86VideoAdaptorPtr newAdaptor = NULL, newBlitAdaptor = NULL;
    int num_adaptors;

    newAdaptor = SISSetupImageVideo(pScreen);
    if(newAdaptor) {
       SISInitOffscreenImages(pScreen);
    }

#ifdef INCL_YUV_BLIT_ADAPTOR
    if( ( (pSiS->ChipFlags & SiSCF_Is65x) ||
          (pSiS->ChipType >= SIS_330) ) &&
        (pScrn->bitsPerPixel != 8) ) {
       newBlitAdaptor = SISSetupBlitVideo(pScreen);
    }
#endif

    num_adaptors = xf86XVListGenericAdaptors(pScrn, &adaptors);

    if(newAdaptor || newBlitAdaptor) {
       int size = num_adaptors;

       if(newAdaptor)     size++;
       if(newBlitAdaptor) size++;

       newAdaptors = malloc(size * sizeof(XF86VideoAdaptorPtr*));
       if(newAdaptors) {
          if(num_adaptors) {
             memcpy(newAdaptors, adaptors, num_adaptors * sizeof(XF86VideoAdaptorPtr));
	  }
	  if(pSiS->XvDefAdaptorBlit) {
	     if(newBlitAdaptor) {
                newAdaptors[num_adaptors] = newBlitAdaptor;
                num_adaptors++;
             }
	  }
	  if(newAdaptor) {
             newAdaptors[num_adaptors] = newAdaptor;
             num_adaptors++;
          }
	  if(!pSiS->XvDefAdaptorBlit) {
	     if(newBlitAdaptor) {
                newAdaptors[num_adaptors] = newBlitAdaptor;
                num_adaptors++;
             }
	  }
	  adaptors = newAdaptors;
       }
    }

    if(num_adaptors) {
       xf86XVScreenInit(pScreen, adaptors, num_adaptors);
    }

    if(newAdaptors) {
       free(newAdaptors);
    }
}

/*********************************
 *       SetPortsDefault()       *
 *********************************/

void
SISSetPortDefaults(ScrnInfoPtr pScrn, SISPortPrivPtr pPriv)
{
    SISPtr    pSiS = SISPTR(pScrn);
#ifdef SISDUALHEAD
    SISEntPtr pSiSEnt = pSiS->entityPrivate;;
#endif

    pPriv->colorKey    = pSiS->colorKey = 0x000101fe;
    pPriv->brightness  = pSiS->XvDefBri;
    pPriv->contrast    = pSiS->XvDefCon;
    pPriv->hue         = pSiS->XvDefHue;
    pPriv->saturation  = pSiS->XvDefSat;
    pPriv->autopaintColorKey = TRUE;
    pPriv->disablegfx  = pSiS->XvDefDisableGfx;
    pPriv->disablegfxlr= pSiS->XvDefDisableGfxLR;
    pSiS->disablecolorkeycurrent = pSiS->XvDisableColorKey;
    pPriv->usechromakey    = pSiS->XvUseChromaKey;
    pPriv->insidechromakey = pSiS->XvInsideChromaKey;
    pPriv->yuvchromakey    = pSiS->XvYUVChromaKey;
    pPriv->chromamin       = pSiS->XvChromaMin;
    pPriv->chromamax       = pSiS->XvChromaMax;
    if(pPriv->dualHeadMode) {
#ifdef SISDUALHEAD
       if(!pSiS->SecondHead) {
          pPriv->tvxpos      = pSiS->tvxpos;
          pPriv->tvypos      = pSiS->tvypos;
	  pPriv->updatetvxpos = TRUE;
          pPriv->updatetvypos = TRUE;
       }
#endif
    } else {
       pPriv->tvxpos      = pSiS->tvxpos;
       pPriv->tvypos      = pSiS->tvypos;
       pPriv->updatetvxpos = TRUE;
       pPriv->updatetvypos = TRUE;
    }
#ifdef SIS_CP
    SIS_CP_VIDEO_DEF
#endif
    if(pPriv->dualHeadMode) {
#ifdef SISDUALHEAD
       pPriv->crtnum =
	  pSiSEnt->curxvcrtnum =
	     pSiSEnt->XvOnCRT2 ? 1 : 0;
#endif
    } else
       pPriv->crtnum = pSiS->XvOnCRT2 ? 1 : 0;

    pSiS->XvGammaRed = pSiS->XvGammaRedDef;
    pSiS->XvGammaGreen = pSiS->XvGammaGreenDef;
    pSiS->XvGammaBlue = pSiS->XvGammaBlueDef;
    SiSUpdateXvGamma(pSiS, pPriv);
}

/*********************************
 *          ResetVideo()         *
 *********************************/

static void
SISResetVideo(ScrnInfoPtr pScrn)
{
    SISPtr pSiS = SISPTR(pScrn);
    SISPortPrivPtr pPriv = GET_PORT_PRIVATE(pScrn);

    /* Unlock registers */
#ifdef UNLOCK_ALWAYS
    sisSaveUnlockExtRegisterLock(pSiS, NULL, NULL);
#endif
    if(getvideoreg (pSiS, Index_VI_Passwd) != 0xa1) {
       setvideoreg (pSiS, Index_VI_Passwd, 0x86);
       if(getvideoreg (pSiS, Index_VI_Passwd) != 0xa1)
	  xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
		 "Xv: Video password could not unlock registers\n");
    }

    /* Initialize first overlay (CRT1) ------------------------------- */

    /* This bit has obviously a different meaning on 315 series (linebuffer-related) */
    if(pSiS->VGAEngine == SIS_300_VGA) {
       /* Write-enable video registers */
       setvideoregmask(pSiS, Index_VI_Control_Misc2,      0x80, 0x81);
    } else {
       /* Select overlay 2, clear all linebuffer related bits */
       setvideoregmask(pSiS, Index_VI_Control_Misc2,      0x00, 0xb1);
    }

    /* Disable overlay */
    setvideoregmask(pSiS, Index_VI_Control_Misc0,         0x00, 0x02);

    /* Disable bob de-interlacer and some strange bit */
    setvideoregmask(pSiS, Index_VI_Control_Misc1,         0x00, 0x82);

    /* Select RGB chroma key format (300 series only) */
    if(pSiS->VGAEngine == SIS_300_VGA) {
       setvideoregmask(pSiS, Index_VI_Control_Misc0,      0x00, 0x40);
    }

    /* Reset scale control and contrast */
    /* (Enable DDA (interpolation)) */
    setvideoregmask(pSiS, Index_VI_Scale_Control,         0x60, 0x60);
    setvideoregmask(pSiS, Index_VI_Contrast_Enh_Ctrl,     0x04, 0x1F);

    setvideoreg(pSiS, Index_VI_Disp_Y_Buf_Preset_Low,     0x00);
    setvideoreg(pSiS, Index_VI_Disp_Y_Buf_Preset_Middle,  0x00);
    setvideoreg(pSiS, Index_VI_UV_Buf_Preset_Low,         0x00);
    setvideoreg(pSiS, Index_VI_UV_Buf_Preset_Middle,      0x00);
    setvideoreg(pSiS, Index_VI_Disp_Y_UV_Buf_Preset_High, 0x00);
    setvideoreg(pSiS, Index_VI_Play_Threshold_Low,        0x00);
    setvideoreg(pSiS, Index_VI_Play_Threshold_High,       0x00);
    if(pSiS->Chipset == PCI_CHIP_SIS330) {
       /* Disable contrast enhancement (?) */
       setvideoregmask(pSiS, Index_VI_Key_Overlay_OP, 0x00, 0x10);
    } else if(pPriv->is661741760) {
       setvideoregmask(pSiS, Index_VI_Key_Overlay_OP, 0x00, 0xE0);
       if(pPriv->is760) {
          setvideoregmask(pSiS, Index_VI_V_Buf_Start_Over, 0x3c, 0x3c);
       } else { /* 661, 741 */
          setvideoregmask(pSiS, Index_VI_V_Buf_Start_Over, 0x2c, 0x3c);
       }
    } else if((pSiS->Chipset == PCI_CHIP_SIS340) ||
	      (pSiS->Chipset == PCI_CHIP_XGIXG20) ||
	      (pSiS->Chipset == PCI_CHIP_XGIXG40)) {
       /* Disable contrast enhancement (?) */
       setvideoregmask(pSiS, Index_VI_Key_Overlay_OP, 0x00, 0x10);
       /* Threshold high */
       setvideoregmask(pSiS, 0xb5, 0x00, 0x01);
       setvideoregmask(pSiS, 0xb6, 0x00, 0x01);
       /* Enable horizontal, disable vertical 4-tap DDA scaler */
       setvideoregmask(pSiS, Index_VI_Key_Overlay_OP, 0x40, 0xc0);
       set_dda_regs(pSiS, 1.0);
       /* Enable software-flip */
       setvideoregmask(pSiS, Index_VI_Key_Overlay_OP, 0x20, 0x20);
       /* "Disable video processor" */
       setsrregmask(pSiS, 0x3f, 0x00, 0x02);
    } else if(pPriv->is761) {
       /* Disable contrast enhancement (?) */
       setvideoregmask(pSiS, Index_VI_Key_Overlay_OP, 0x00, 0x10);
       /* Threshold high */
       setvideoregmask(pSiS, 0xb5, 0x00, 0x01);
       setvideoregmask(pSiS, 0xb6, 0x00, 0x01);
       /* Enable horizontal, disable vertical 4-tap DDA scaler */
       setvideoregmask(pSiS, Index_VI_Key_Overlay_OP, 0x40, 0xC0);
       /* ? */
       setvideoregmask(pSiS, 0xb6, 0x02, 0x02);
       set_dda_regs(pSiS, 1.0);
       setvideoregmask(pSiS, Index_VI_V_Buf_Start_Over, 0x00, 0x3c);
    }

    if((pSiS->ChipFlags & SiSCF_Is65x) || (pPriv->is661741760)) {
       setvideoregmask(pSiS, Index_VI_Control_Misc2,  0x00, 0x04);
    }

    /* Reset top window position for scanline check */
    setvideoreg(pSiS, Index_VI_Win_Ver_Disp_Start_Low, 0x00);
    setvideoreg(pSiS, Index_VI_Win_Ver_Over, 0x00);

    /* Initialize second overlay (CRT2) - only for 300, 630/730, 550, M650/651, 661/741/660/760 */
    if(pSiS->hasTwoOverlays) {

	if(pSiS->VGAEngine == SIS_300_VGA) {
	   /* Write-enable video registers */
	   setvideoregmask(pSiS, Index_VI_Control_Misc2,      0x81, 0x81);
	} else {
	   /* Select overlay 2, clear all linebuffer related bits */
	   setvideoregmask(pSiS, Index_VI_Control_Misc2,      0x01, 0xb1);
	}

	/* Disable overlay */
	setvideoregmask(pSiS, Index_VI_Control_Misc0,         0x00, 0x02);

	/* Disable bob de-interlacer and some strange bit */
	setvideoregmask(pSiS, Index_VI_Control_Misc1,         0x00, 0x82);

	/* Select RGB chroma key format */
	if(pSiS->VGAEngine == SIS_300_VGA) {
	   setvideoregmask(pSiS, Index_VI_Control_Misc0,      0x00, 0x40);
	}

	/* Reset scale control and contrast */
	/* (Enable DDA (interpolation)) */
	setvideoregmask(pSiS, Index_VI_Scale_Control,         0x60, 0x60);
	setvideoregmask(pSiS, Index_VI_Contrast_Enh_Ctrl,     0x04, 0x1F);

	setvideoreg(pSiS, Index_VI_Disp_Y_Buf_Preset_Low,     0x00);
	setvideoreg(pSiS, Index_VI_Disp_Y_Buf_Preset_Middle,  0x00);
	setvideoreg(pSiS, Index_VI_UV_Buf_Preset_Low,         0x00);
	setvideoreg(pSiS, Index_VI_UV_Buf_Preset_Middle,      0x00);
	setvideoreg(pSiS, Index_VI_Disp_Y_UV_Buf_Preset_High, 0x00);
	setvideoreg(pSiS, Index_VI_Play_Threshold_Low,        0x00);
	setvideoreg(pSiS, Index_VI_Play_Threshold_High,       0x00);

	if(pPriv->is661741760) {
	   CARD8 temp;
	   setvideoregmask(pSiS, Index_VI_Key_Overlay_OP, 0x00, 0xE0);
	   switch(pSiS->ChipType) {
	   case SIS_661: temp = 0x24; break;
	   case SIS_741: temp = 0x2c; break;
	   default: 	 temp = 0x3c;
	   }
	   setvideoregmask(pSiS, Index_VI_V_Buf_Start_Over, temp, 0x3c);
	} else if(pPriv->is761) {
	   setvideoregmask(pSiS, Index_VI_V_Buf_Start_Over, 0x00, 0x3c);
	} else if(pSiS->Chipset == PCI_CHIP_SIS340) {  /* 2 overlays? */
	   setvideoregmask(pSiS, Index_VI_Key_Overlay_OP, 0x00, 0x10);
	   setvideoregmask(pSiS, 0xb5, 0x00, 0x01);
	   setvideoregmask(pSiS, 0xb6, 0x00, 0x01);
	   setvideoregmask(pSiS, Index_VI_Key_Overlay_OP, 0x40, 0xC0);
	   set_dda_regs(pSiS, 1.0);
           setvideoregmask(pSiS, Index_VI_Key_Overlay_OP, 0x20, 0x20);
	}

	setvideoreg(pSiS, Index_VI_Win_Ver_Disp_Start_Low, 0x00);
	setvideoreg(pSiS, Index_VI_Win_Ver_Over, 0x00);

    }

    /* set default properties for overlay 1 (CRT1) -------------------------- */
    setvideoregmask(pSiS, Index_VI_Control_Misc2,         0x00, 0x01);
    setvideoregmask(pSiS, Index_VI_Contrast_Enh_Ctrl,     0x04, 0x07);
    setvideoreg(pSiS, Index_VI_Brightness,                0x20);
    if(pSiS->VGAEngine == SIS_315_VGA) {
       setvideoreg(pSiS, Index_VI_Hue,          	  0x00);
       setvideoreg(pSiS, Index_VI_Saturation,             0x00);
    }

    /* set default properties for overlay 2(CRT2)  -------------------------- */
    if(pSiS->hasTwoOverlays) {
       setvideoregmask(pSiS, Index_VI_Control_Misc2,      0x01, 0x01);
       setvideoregmask(pSiS, Index_VI_Contrast_Enh_Ctrl,  0x04, 0x07);
       setvideoreg(pSiS, Index_VI_Brightness,             0x20);
       if(pSiS->VGAEngine == SIS_315_VGA) {
          setvideoreg(pSiS, Index_VI_Hue,                 0x00);
          setvideoreg(pSiS, Index_VI_Saturation,    	  0x00);
       }
    }

    /* Reset Xv gamma correction */
    if(pSiS->VGAEngine == SIS_315_VGA) {
       SiSUpdateXvGamma(pSiS, pPriv);
    }

    pPriv->mustresettap = TRUE;
#ifdef SISMERGED
    pPriv->mustresettap2 = TRUE;
#endif
}

/*********************************
 *       Set displaymode         *
 *********************************/

/* Set display mode (single CRT1/CRT2, mirror).
 * MIRROR mode is only available on chipsets with two overlays.
 * On the other chipsets, if only CRT1 or only CRT2 are used,
 * the correct display CRT is chosen automatically. If both
 * CRT1 and CRT2 are connected, the user can choose between CRT1 and
 * CRT2 by using the option XvOnCRT2.
 */

static void
set_dispmode(ScrnInfoPtr pScrn, SISPortPrivPtr pPriv)
{
    SISPtr pSiS = SISPTR(pScrn);

    pPriv->dualHeadMode = pPriv->bridgeIsSlave = FALSE;

    if(SiSBridgeIsInSlaveMode(pScrn)) {
       pPriv->bridgeIsSlave = TRUE;
    }

    if( (pSiS->VBFlags & VB_DISPMODE_MIRROR) ||
        ((pPriv->bridgeIsSlave) && (pSiS->VBFlags & DISPTYPE_DISP2)) )  {
       if(pPriv->hasTwoOverlays)
	   pPriv->displayMode = DISPMODE_MIRROR;     /* CRT1+CRT2 (2 overlays) */
       else if(pPriv->crtnum)
	  pPriv->displayMode = DISPMODE_SINGLE2;    /* CRT2 only */
       else
	  pPriv->displayMode = DISPMODE_SINGLE1;    /* CRT1 only */
    } else {
#ifdef SISDUALHEAD
       if(pSiS->DualHeadMode) {
	  pPriv->dualHeadMode = TRUE;
	  if(pSiS->SecondHead)
	     pPriv->displayMode = DISPMODE_SINGLE1; /* CRT1 only */
	  else
	     pPriv->displayMode = DISPMODE_SINGLE2; /* CRT2 only */
       } else
#endif
       if(pSiS->VBFlags & DISPTYPE_DISP1) {
	  pPriv->displayMode = DISPMODE_SINGLE1;    /* CRT1 only */
       } else {
	  pPriv->displayMode = DISPMODE_SINGLE2;    /* CRT2 only */
       }
    }
}

static void
set_disptype_regs(ScrnInfoPtr pScrn, SISPortPrivPtr pPriv)
{
    SISPtr pSiS = SISPTR(pScrn);
#ifdef SISDUALHEAD
    SISEntPtr pSiSEnt = pSiS->entityPrivate;
    int crtnum = 0;

    if(pPriv->dualHeadMode) crtnum = pSiSEnt->curxvcrtnum;
#endif

    /*
     *     SR06[7:6]
     *	      Bit 7: Enable overlay 1 on CRT2
     *	      Bit 6: Enable overlay 0 on CRT2
     *     SR32[7:6]
     *        Bit 7: DCLK/TCLK overlay 1
     *               0=DCLK (overlay on CRT1)
     *               1=TCLK (overlay on CRT2)
     *        Bit 6: DCLK/TCLK overlay 0
     *               0=DCLK (overlay on CRT1)
     *               1=TCLK (overlay on CRT2)
     *
     * On chipsets with two overlays, we can freely select and also
     * have a mirror mode. However, we use overlay 0 for CRT1 and
     * overlay 1 for CRT2.
     * ATTENTION: CRT2 can only take up to 1 (one) overlay. Setting
     * SR06/32 to 0xc0 DOES NOT WORK. THAT'S CONFIRMED.
     * Therefore, we use overlay 0 on CRT2 if in SINGLE2 mode.
     *
     * For chipsets with only one overlay, user must choose whether
     * to display the overlay on CRT1 or CRT2 by setting XvOnCRT2
     * to TRUE (CRT2) or FALSE (CRT1). The driver does this auto-
     * matically if only CRT1 or only CRT2 is used.
     */
#ifdef UNLOCK_ALWAYS
    sisSaveUnlockExtRegisterLock(pSiS, NULL, NULL);
#endif

    switch (pPriv->displayMode)
    {
       case DISPMODE_SINGLE1:				/* CRT1-only mode: */
	  if(pPriv->hasTwoOverlays) {
	      if(pPriv->dualHeadMode) {
		 setsrregmask(pSiS, 0x06, 0x00, 0x40);  /* overlay 0 -> CRT1 */
		 setsrregmask(pSiS, 0x32, 0x00, 0x40);
	      } else {
		 setsrregmask(pSiS, 0x06, 0x00, 0xc0);  /* both overlays -> CRT1 */
		 setsrregmask(pSiS, 0x32, 0x00, 0xc0);
	      }
	  } else {
#ifdef SISDUALHEAD
	      if((!pPriv->dualHeadMode) || (crtnum == 0)) {
#endif
		 setsrregmask(pSiS, 0x06, 0x00, 0xc0);  /* only overlay -> CRT1 */
		 setsrregmask(pSiS, 0x32, 0x00, 0xc0);
#ifdef SISDUALHEAD
	      }
#endif
	  }
	  break;

       case DISPMODE_SINGLE2:			/* CRT2-only mode: */
	  if(pPriv->hasTwoOverlays) {
	      if(pPriv->dualHeadMode) {
		 setsrregmask(pSiS, 0x06, 0x80, 0x80);  /* overlay 1 -> CRT2 */
		 setsrregmask(pSiS, 0x32, 0x80, 0x80);
	      } else {
		 setsrregmask(pSiS, 0x06, 0x40, 0xc0);  /* overlay 0 -> CRT2 */
		 setsrregmask(pSiS, 0x32, 0xc0, 0xc0);  /* (although both clocks for CRT2!) */
	      }
	  } else {
#ifdef SISDUALHEAD
	      if((!pPriv->dualHeadMode) || (crtnum == 1)) {
#endif
		 if(pSiS->MiscFlags & MISC_SIS760ONEOVERLAY) {
		    setsrregmask(pSiS, 0x06, 0x40, 0xc0);  /* overlay 0 -> CRT2 */
		    setsrregmask(pSiS, 0x32, 0xc0, 0xc0);  /* (although both clocks for CRT2!) */
		 } else {
		    setsrregmask(pSiS, 0x06, 0x40, 0xc0);  /* only overlay -> CRT2 */
		    setsrregmask(pSiS, 0x32, 0x40, 0xc0);
		 }
#ifdef SISDUALHEAD
              }
#endif
	  }
	  break;

       case DISPMODE_MIRROR:				/* CRT1+CRT2-mode: (only on chips with 2 overlays) */
       default:
	  setsrregmask(pSiS, 0x06, 0x80, 0xc0);         /* overlay 0 -> CRT1, overlay 1 -> CRT2 */
	  setsrregmask(pSiS, 0x32, 0x80, 0xc0);
	  break;
    }
}

static void
set_hastwooverlays(SISPtr pSiS, SISPortPrivPtr pPriv)
{
    int temp, watchdog;

    if(pSiS->hasTwoOverlays) {
       if(pSiS->MiscFlags & MISC_SIS760ONEOVERLAY) {
          if(pPriv->hasTwoOverlays) {
	     /* Disable overlay 1 on change */
	     setvideoregmask(pSiS, Index_VI_Control_Misc2, 0x01, 0x01);
	     setvideoregmask(pSiS, Index_VI_Control_Misc1, 0x00, 0x01);
	     temp = getvideoreg(pSiS,Index_VI_Control_Misc0);
	     if(temp & 0x02) {
	        watchdog = WATCHDOG_DELAY;
	        while((!vblank_active_CRT2(pSiS, pPriv)) && --watchdog);
	        watchdog = WATCHDOG_DELAY;
	        while(vblank_active_CRT2(pSiS, pPriv) && --watchdog);
	        setvideoregmask(pSiS, Index_VI_Control_Misc0, 0x00, 0x02);
	     }
	  }
          pPriv->hasTwoOverlays = FALSE;
       } else {
          pPriv->hasTwoOverlays = TRUE;
       }
    } else {
       pPriv->hasTwoOverlays = FALSE;
    }
}

static void
set_allowswitchcrt(SISPtr pSiS, SISPortPrivPtr pPriv)
{
    if(pPriv->hasTwoOverlays) {
       pPriv->AllowSwitchCRT = FALSE;
    } else if((!(pSiS->VBFlags & DISPTYPE_DISP1)) || (!(pSiS->VBFlags & DISPTYPE_DISP2))) {
       pPriv->AllowSwitchCRT = FALSE;
       if(!(pSiS->VBFlags & DISPTYPE_DISP1)) pPriv->crtnum = 1;
       else                                  pPriv->crtnum = 0;
    } else {
       pPriv->AllowSwitchCRT = TRUE;
    }
}

static void
set_maxencoding(SISPtr pSiS, SISPortPrivPtr pPriv)
{
    int half;

    if(pSiS->VGAEngine == SIS_300_VGA) {
       DummyEncoding.width = IMAGE_MAX_WIDTH_300;
       DummyEncoding.height = IMAGE_MAX_HEIGHT_300;
    } else {
       DummyEncoding.width = IMAGE_MAX_WIDTH_315;
       DummyEncoding.height = IMAGE_MAX_HEIGHT_315;
       half = IMAGE_MAX_WIDTH_315 >> 1;
       if(pPriv->is661741760) {
          half = 768 * 2;
       } else if(pPriv->is340) { /* 2 overlays? */
          DummyEncoding.width = IMAGE_MAX_WIDTH_340;
	  half = 1280; /* ? */
       } else if(pPriv->is761) {
          DummyEncoding.width = IMAGE_MAX_WIDTH_761;
          half = 1920; /* ? */
       }
       if(pPriv->hasTwoOverlays) {
#ifdef SISDUALHEAD
          if(pSiS->DualHeadMode) {
	     DummyEncoding.width = half;
          } else
#endif
#ifdef SISMERGED
          if(pSiS->MergedFB) {
	     DummyEncoding.width = half;
          } else
#endif
          if(pPriv->displayMode == DISPMODE_MIRROR) {
	     DummyEncoding.width = half;
          }
       }
    }
}

/*********************************
 *       ResetXvDisplay()        *
 *********************************/

static void
SISResetXvDisplay(ScrnInfoPtr pScrn)
{
   SISPtr pSiS = SISPTR(pScrn);
   SISPortPrivPtr pPriv = GET_PORT_PRIVATE(pScrn);

   if(!pPriv) return;

   set_hastwooverlays(pSiS, pPriv);
   set_allowswitchcrt(pSiS, pPriv);
   set_dispmode(pScrn, pPriv);
   set_maxencoding(pSiS, pPriv);
}

/*********************************
 *       SetupImageVideo()       *
 *********************************/

static XF86VideoAdaptorPtr
SISSetupImageVideo(ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    SISPtr pSiS = SISPTR(pScrn);
    XF86VideoAdaptorPtr adapt;
    SISPortPrivPtr pPriv;

#if XF86_VERSION_CURRENT < XF86_VERSION_NUMERIC(4,1,99,1,0)
    XAAInfoRecPtr pXAA = pSiS->AccelInfoPtr;

    if(!pXAA || !pXAA->FillSolidRects) {
       return NULL;
    }
#endif

    if(!(adapt = calloc(1, sizeof(XF86VideoAdaptorRec) +
                            sizeof(SISPortPrivRec) +
                            sizeof(DevUnion)))) {
       return NULL;
    }

    adapt->type = XvWindowMask | XvInputMask | XvImageMask;
    adapt->flags = VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT;
    adapt->name = "SIS 300/315/330 series Video Overlay";
    adapt->nEncodings = 1;
    adapt->pEncodings = &DummyEncoding;

    adapt->nFormats = NUM_FORMATS;
    adapt->pFormats = SISFormats;
    adapt->nPorts = 1;
    adapt->pPortPrivates = (DevUnion*)(&adapt[1]);

    pPriv = (SISPortPrivPtr)(&adapt->pPortPrivates[1]);

    pPriv->videoStatus = 0;
    pPriv->currentBuf  = 0;
    pPriv->handle      = NULL;
    pPriv->grabbedByV4L= FALSE;
    pPriv->NoOverlay   = FALSE;
    pPriv->PrevOverlay = FALSE;
    pPriv->is661741760 = ((pSiS->ChipType >= SIS_661) &&
			  (pSiS->ChipType <= SIS_760)) ? TRUE : FALSE;
    pPriv->is760       = (pSiS->ChipType == SIS_760)   ? TRUE : FALSE;
    pPriv->is761       = (pSiS->ChipType == SIS_761)   ? TRUE : FALSE;
    pPriv->is340       = (pSiS->Chipset == PCI_CHIP_SIS340) ? TRUE : FALSE;
    pPriv->isXGI       = (pSiS->Chipset == PCI_CHIP_XGIXG20 ||
			  pSiS->Chipset == PCI_CHIP_XGIXG40) ? TRUE : FALSE;

    /* Setup chipset type helpers */
    set_hastwooverlays(pSiS, pPriv);
    set_allowswitchcrt(pSiS, pPriv);

    pPriv->havetapscaler = FALSE;
    if(pPriv->is340 || pPriv->is761 || pPriv->isXGI) {
       pPriv->havetapscaler = TRUE;
    }

    adapt->pPortPrivates[0].ptr = (pointer)(pPriv);
    if(pSiS->VGAEngine == SIS_300_VGA) {
       adapt->nImages = NUM_IMAGES_300;
       adapt->pAttributes = SISAttributes_300;
       adapt->nAttributes = SiSCountAttributes(&SISAttributes_300[0]);
    } else {
       if(pSiS->ChipType >= SIS_330) {
          adapt->nImages = NUM_IMAGES_330;
       } else {
          adapt->nImages = NUM_IMAGES_315;
       }
       adapt->pAttributes = SISAttributes_315;
       adapt->nAttributes = SiSCountAttributes(&SISAttributes_315[0]);
       if((pSiS->hasTwoOverlays) && (!(pSiS->SiS_SD2_Flags & SiS_SD2_SUPPORT760OO))) {
          adapt->nAttributes--;
       }
    }

    adapt->pImages = SISImages;
    adapt->PutVideo = NULL;
    adapt->PutStill = NULL;
    adapt->GetVideo = NULL;
    adapt->GetStill = NULL;
    adapt->StopVideo = SISStopVideo;
    adapt->SetPortAttribute = SISSetPortAttribute;
    adapt->GetPortAttribute = SISGetPortAttribute;
    adapt->QueryBestSize = SISQueryBestSize;
    adapt->PutImage = SISPutImage;
    adapt->QueryImageAttributes = SISQueryImageAttributes;

    /* gotta uninit this someplace */
#if defined(REGION_NULL)
    REGION_NULL(pScreen, &pPriv->clip);
#else
    REGION_INIT(pScreen, &pPriv->clip, NullBox, 0);
#endif

    pSiS->adaptor = adapt;

    pSiS->xvBrightness        = MAKE_ATOM(sisxvbrightness);
    pSiS->xvContrast          = MAKE_ATOM(sisxvcontrast);
    pSiS->xvColorKey          = MAKE_ATOM(sisxvcolorkey);
    pSiS->xvSaturation        = MAKE_ATOM(sisxvsaturation);
    pSiS->xvHue               = MAKE_ATOM(sisxvhue);
    pSiS->xvSwitchCRT  	      = MAKE_ATOM(sisxvswitchcrt);
    pSiS->xvAutopaintColorKey = MAKE_ATOM(sisxvautopaintcolorkey);
    pSiS->xvSetDefaults       = MAKE_ATOM(sisxvsetdefaults);
    pSiS->xvDisableGfx        = MAKE_ATOM(sisxvdisablegfx);
    pSiS->xvDisableGfxLR      = MAKE_ATOM(sisxvdisablegfxlr);
    pSiS->xvTVXPosition       = MAKE_ATOM(sisxvtvxposition);
    pSiS->xvTVYPosition       = MAKE_ATOM(sisxvtvyposition);
    pSiS->xvGammaRed	      = MAKE_ATOM(sisxvgammared);
    pSiS->xvGammaGreen	      = MAKE_ATOM(sisxvgammagreen);
    pSiS->xvGammaBlue	      = MAKE_ATOM(sisxvgammablue);
    pSiS->xvDisableColorkey   = MAKE_ATOM(sisxvdisablecolorkey);
    pSiS->xvUseChromakey      = MAKE_ATOM(sisxvusechromakey);
    pSiS->xvInsideChromakey   = MAKE_ATOM(sisxvinsidechromakey);
    pSiS->xvYUVChromakey      = MAKE_ATOM(sisxvyuvchromakey);
    pSiS->xvChromaMin	      = MAKE_ATOM(sisxvchromamin);
    pSiS->xvChromaMax         = MAKE_ATOM(sisxvchromamax);
#ifdef SISDEINT
    pSiS->xvdeintmeth	      = MAKE_ATOM(sisxvdeinterlace);
#endif
#ifdef XV_SD_DEPRECATED
    pSiS->xv_QVF              = MAKE_ATOM(sisxvqueryvbflags);
    pSiS->xv_GDV	      = MAKE_ATOM(sisxvsdgetdriverversion);
    pSiS->xv_GHI	      = MAKE_ATOM(sisxvsdgethardwareinfo);
    pSiS->xv_GBI	      = MAKE_ATOM(sisxvsdgetbusid);
    pSiS->xv_QVV              = MAKE_ATOM(sisxvsdqueryvbflagsversion);
    pSiS->xv_GSF              = MAKE_ATOM(sisxvsdgetsdflags);
    pSiS->xv_GSF2             = MAKE_ATOM(sisxvsdgetsdflags2);
    pSiS->xv_USD              = MAKE_ATOM(sisxvsdunlocksisdirect);
    pSiS->xv_SVF              = MAKE_ATOM(sisxvsdsetvbflags);
    pSiS->xv_QDD	      = MAKE_ATOM(sisxvsdquerydetecteddevices);
    pSiS->xv_CT1	      = MAKE_ATOM(sisxvsdcrt1status);
    pSiS->xv_CMD	      = MAKE_ATOM(sisxvsdcheckmodeindexforcrt2);
    pSiS->xv_CMDR	      = MAKE_ATOM(sisxvsdresultcheckmodeindexforcrt2);
    pSiS->xv_RDT	      = MAKE_ATOM(sisxvsdredetectcrt2);
    pSiS->xv_TAF	      = MAKE_ATOM(sisxvsdsisantiflicker);
    pSiS->xv_TSA	      = MAKE_ATOM(sisxvsdsissaturation);
    pSiS->xv_TEE	      = MAKE_ATOM(sisxvsdsisedgeenhance);
    pSiS->xv_COC	      = MAKE_ATOM(sisxvsdsiscolcalibc);
    pSiS->xv_COF	      = MAKE_ATOM(sisxvsdsiscolcalibf);
    pSiS->xv_CFI	      = MAKE_ATOM(sisxvsdsiscfilter);
    pSiS->xv_YFI	      = MAKE_ATOM(sisxvsdsisyfilter);
    pSiS->xv_TCO	      = MAKE_ATOM(sisxvsdchcontrast);
    pSiS->xv_TTE	      = MAKE_ATOM(sisxvsdchtextenhance);
    pSiS->xv_TCF	      = MAKE_ATOM(sisxvsdchchromaflickerfilter);
    pSiS->xv_TLF	      = MAKE_ATOM(sisxvsdchlumaflickerfilter);
    pSiS->xv_TCC	      = MAKE_ATOM(sisxvsdchcvbscolor);
    pSiS->xv_OVR	      = MAKE_ATOM(sisxvsdchoverscan);
    pSiS->xv_SGA	      = MAKE_ATOM(sisxvsdenablegamma);
    pSiS->xv_TXS	      = MAKE_ATOM(sisxvsdtvxscale);
    pSiS->xv_TYS	      = MAKE_ATOM(sisxvsdtvyscale);
    pSiS->xv_GSS	      = MAKE_ATOM(sisxvsdgetscreensize);
    pSiS->xv_BRR	      = MAKE_ATOM(sisxvsdstorebrir);
    pSiS->xv_BRG	      = MAKE_ATOM(sisxvsdstorebrig);
    pSiS->xv_BRB	      = MAKE_ATOM(sisxvsdstorebrib);
    pSiS->xv_PBR	      = MAKE_ATOM(sisxvsdstorepbrir);
    pSiS->xv_PBG	      = MAKE_ATOM(sisxvsdstorepbrig);
    pSiS->xv_PBB	      = MAKE_ATOM(sisxvsdstorepbrib);
    pSiS->xv_BRR2	      = MAKE_ATOM(sisxvsdstorebrir2);
    pSiS->xv_BRG2	      = MAKE_ATOM(sisxvsdstorebrig2);
    pSiS->xv_BRB2	      = MAKE_ATOM(sisxvsdstorebrib2);
    pSiS->xv_PBR2	      = MAKE_ATOM(sisxvsdstorepbrir2);
    pSiS->xv_PBG2	      = MAKE_ATOM(sisxvsdstorepbrig2);
    pSiS->xv_PBB2	      = MAKE_ATOM(sisxvsdstorepbrib2);
    pSiS->xv_GARC2	      = MAKE_ATOM(sisxvsdstoregarc2);
    pSiS->xv_GAGC2	      = MAKE_ATOM(sisxvsdstoregagc2);
    pSiS->xv_GABC2	      = MAKE_ATOM(sisxvsdstoregabc2);
    pSiS->xv_BRRC2	      = MAKE_ATOM(sisxvsdstorebrirc2);
    pSiS->xv_BRGC2	      = MAKE_ATOM(sisxvsdstorebrigc2);
    pSiS->xv_BRBC2	      = MAKE_ATOM(sisxvsdstorebribc2);
    pSiS->xv_PBRC2	      = MAKE_ATOM(sisxvsdstorepbrirc2);
    pSiS->xv_PBGC2	      = MAKE_ATOM(sisxvsdstorepbrigc2);
    pSiS->xv_PBBC2	      = MAKE_ATOM(sisxvsdstorepbribc2);
    pSiS->xv_SHC	      = MAKE_ATOM(sisxvsdhidehwcursor);
    pSiS->xv_PMD	      = MAKE_ATOM(sisxvsdpanelmode);
#ifdef TWDEBUG
    pSiS->xv_STR	      = MAKE_ATOM(sisxvsetreg);
#endif
#endif /* XV_SD_DEPRECATED */
#ifdef SIS_CP
    SIS_CP_VIDEO_ATOMS
#endif

    pSiS->xv_sisdirectunlocked = 0;
#ifdef XV_SD_DEPRECATED
    pSiS->xv_sd_result = 0;
#endif

    /* 300 series require double words for addresses and pitches,
     * 315/330 series require word.
     */
    switch (pSiS->VGAEngine) {
    case SIS_315_VGA:
	pPriv->shiftValue = 1;
	break;
    case SIS_300_VGA:
    default:
	pPriv->shiftValue = 2;
	break;
    }

    /* Set displayMode according to VBFlags */
    set_dispmode(pScrn, pPriv);

    /* Now for the linebuffer stuff.
     * All chipsets have a certain number of linebuffers, each of a certain
     * size. The number of buffers is per overlay.
     * Chip        number      size     	  max video size
     *  300          2		 ?		     720x576
     *  630/730      2		 ?		     720x576
     *  315          2		960?		    1920x1080
     *  550	     2?		960?		    1920x1080?
     *  650/740      2		960 ("120x128")	    1920x1080
     *  M650/651..   4		480		    1920x1080
     *  330          2		960		    1920x1080
     *  661/741/760  4		768 		    1920x1080
     *  340          4	       1280?		    1920x1080?
     *  761          4         1536?		    1920x1080?
     * The unit of size is unknown; I just know that a size of 480 limits
     * the video source width to 384. Beyond that, line buffers must be
     * merged (otherwise the video output is garbled).
     * To use the maximum width (eg 1920x1080 on the 315 series, including
     * the M650, 651 and later), *all* line buffers must be merged. Hence,
     * we can only use one overlay. This should be set up for modes where
     * either only CRT1 or only CRT2 is used.
     * If both overlays are going to be used (such as in modes were both
     * CRT1 and CRT2 are active), we are limited to the half of the
     * maximum width, or 1536 on 661/741/760.
     * There is a known hardware problem with the 760 and 761 if the video
     * data is in the UMA area: The memory access latency is too big to
     * allow two overlays under some circumstances. Therefore, we must
     * support switching between hasTwoOverlays and !hasTwoOverlays on
     * the fly.
     */

    if(pSiS->VGAEngine == SIS_300_VGA) {
       pPriv->linebufmask = 0x11;
       pPriv->linebufMergeLimit = 384;
    } else {
       pPriv->linebufmask = 0xb1;
       pPriv->linebufMergeLimit = 384;			/* should be 480 */
       if(pPriv->is661741760) {
          pPriv->linebufMergeLimit = 576;		/* should be 768 */
       } else if(pPriv->is340) {
          pPriv->linebufMergeLimit = 1280;		/* should be 1280 */
       } else if(pPriv->is761) {
          pPriv->linebufMergeLimit = 1280;		/* should be 1536 */
       } else if(pPriv->isXGI) {
          pPriv->linebufMergeLimit = 1280;		/* FIXME */
       } else if(!(pPriv->hasTwoOverlays)) {
          pPriv->linebufMergeLimit = 720;		/* should be 960 */
       }
       /* No special treatment for 760/761 required */
    }

    set_maxencoding(pSiS, pPriv);

    /* Reset the properties to their defaults */
    SISSetPortDefaults(pScrn, pPriv);

    /* Set SR(06, 32) registers according to DISPMODE */
    set_disptype_regs(pScrn, pPriv);

    SISResetVideo(pScrn);
    pSiS->ResetXv = SISResetVideo;
    pSiS->ResetXvDisplay = SISResetXvDisplay;
    if(pSiS->VGAEngine == SIS_315_VGA) {
       pSiS->ResetXvGamma = SISResetXvGamma;
    }

    return adapt;
}

#if XF86_VERSION_CURRENT < XF86_VERSION_NUMERIC(4,3,99,3,0)
static Bool
RegionsEqual(RegionPtr A, RegionPtr B)
{
    int *dataA, *dataB;
    int num;

    num = REGION_NUM_RECTS(A);
    if(num != REGION_NUM_RECTS(B))
       return FALSE;

    if((A->extents.x1 != B->extents.x1) ||
       (A->extents.x2 != B->extents.x2) ||
       (A->extents.y1 != B->extents.y1) ||
       (A->extents.y2 != B->extents.y2))
       return FALSE;

    dataA = (int*)REGION_RECTS(A);
    dataB = (int*)REGION_RECTS(B);

    while(num--) {
      if((dataA[0] != dataB[0]) || (dataA[1] != dataB[1]))
         return FALSE;
      dataA += 2;
      dataB += 2;
    }

    return TRUE;
}
#endif

/*********************************
 *       SetPortAttribute()      *
 *********************************/

void
SISUpdateVideoParms(SISPtr pSiS, SISPortPrivPtr pPriv)
{
  set_hastwooverlays(pSiS, pPriv);
  set_allowswitchcrt(pSiS, pPriv);
  set_dispmode(pSiS->pScrn, pPriv);
  set_maxencoding(pSiS, pPriv);
}

static int
SISSetPortAttribute(ScrnInfoPtr pScrn, Atom attribute,
		    INT32 value, pointer data)
{
  SISPortPrivPtr pPriv = (SISPortPrivPtr)data;
  SISPtr pSiS = SISPTR(pScrn);
#ifdef SISDUALHEAD
  SISEntPtr pSiSEnt = pSiS->entityPrivate;;
#endif

  if(attribute == pSiS->xvBrightness) {
     if((value < -128) || (value > 127))
        return BadValue;
     pPriv->brightness = value;
  } else if(attribute == pSiS->xvContrast) {
     if((value < 0) || (value > 7))
        return BadValue;
     pPriv->contrast = value;
  } else if(attribute == pSiS->xvColorKey) {
     pPriv->colorKey = pSiS->colorKey = value;
     REGION_EMPTY(pScrn->pScreen, &pPriv->clip);
  } else if(attribute == pSiS->xvAutopaintColorKey) {
     if((value < 0) || (value > 1))
        return BadValue;
     pPriv->autopaintColorKey = value;
  } else if(attribute == pSiS->xvSetDefaults) {
     SISSetPortDefaults(pScrn, pPriv);
  } else if(attribute == pSiS->xvDisableGfx) {
     if((value < 0) || (value > 1))
        return BadValue;
     pPriv->disablegfx = value;
  } else if(attribute == pSiS->xvDisableGfxLR) {
     if((value < 0) || (value > 1))
        return BadValue;
     pPriv->disablegfxlr = value;
  } else if(attribute == pSiS->xvTVXPosition) {
     if((value < -32) || (value > 32))
        return BadValue;
     pPriv->tvxpos = value;
     if(pSiS->xv_sisdirectunlocked) {
        SiS_SetTVxposoffset(pScrn, pPriv->tvxpos);
        pPriv->updatetvxpos = FALSE;
     } else {
        pSiS->tvxpos = pPriv->tvxpos;
#ifdef SISDUALHEAD
        if(pPriv->dualHeadMode) pSiSEnt->tvxpos = pPriv->tvxpos;
#endif
        pPriv->updatetvxpos = TRUE;
     }
  } else if(attribute == pSiS->xvTVYPosition) {
     if((value < -32) || (value > 32)) return BadValue;
     pPriv->tvypos = value;
     if(pSiS->xv_sisdirectunlocked) {
        SiS_SetTVyposoffset(pScrn, pPriv->tvypos);
        pPriv->updatetvypos = FALSE;
     } else {
        pSiS->tvypos = pPriv->tvypos;
#ifdef SISDUALHEAD
        if(pPriv->dualHeadMode) pSiSEnt->tvypos = pPriv->tvypos;
#endif
        pPriv->updatetvypos = TRUE;
     }
  } else if(attribute == pSiS->xvDisableColorkey) {
     if((value < 0) || (value > 1)) return BadValue;
     pSiS->disablecolorkeycurrent = value;
  } else if(attribute == pSiS->xvUseChromakey) {
     if((value < 0) || (value > 1)) return BadValue;
     pPriv->usechromakey = value;
  } else if(attribute == pSiS->xvInsideChromakey) {
     if((value < 0) || (value > 1)) return BadValue;
     pPriv->insidechromakey = value;
  } else if(attribute == pSiS->xvYUVChromakey) {
     if((value < 0) || (value > 1)) return BadValue;
     pPriv->yuvchromakey = value;
  } else if(attribute == pSiS->xvChromaMin) {
     pPriv->chromamin = value;
  } else if(attribute == pSiS->xvChromaMax) {
     pPriv->chromamax = value;
#ifdef SISDEINT
  } else if(attribute == pSiS->xvdeintmeth) {
     if(value < 0) value = 0;
     if(value > 4) value = 4;
     pPriv->deinterlacemethod = value;
#endif
#ifdef SIS_CP
  SIS_CP_VIDEO_SETATTRIBUTE
#endif
  } else if(attribute == pSiS->xvHue) {
     if(pSiS->VGAEngine == SIS_315_VGA) {
        if((value < -8) || (value > 7)) return BadValue;
        pPriv->hue = value;
     } else return BadMatch;
  } else if(attribute == pSiS->xvSaturation) {
     if(pSiS->VGAEngine == SIS_315_VGA) {
        if((value < -7) || (value > 7)) return BadValue;
        pPriv->saturation = value;
     } else return BadMatch;
  } else if(attribute == pSiS->xvGammaRed) {
     if(pSiS->VGAEngine == SIS_315_VGA) {
        if((value < 100) || (value > 10000))  return BadValue;
        pSiS->XvGammaRed = value;
        SiSUpdateXvGamma(pSiS, pPriv);
     } else return BadMatch;
  } else if(attribute == pSiS->xvGammaGreen) {
     if(pSiS->VGAEngine == SIS_315_VGA) {
        if((value < 100) || (value > 10000)) return BadValue;
        pSiS->XvGammaGreen = value;
        SiSUpdateXvGamma(pSiS, pPriv);
     } else return BadMatch;
  } else if(attribute == pSiS->xvGammaBlue) {
     if(pSiS->VGAEngine == SIS_315_VGA) {
        if((value < 100) || (value > 10000)) return BadValue;
        pSiS->XvGammaBlue = value;
        SiSUpdateXvGamma(pSiS, pPriv);
     } else return BadMatch;
  } else if(attribute == pSiS->xvSwitchCRT) {
     if(pSiS->VGAEngine == SIS_315_VGA) {
        if(pPriv->AllowSwitchCRT) {
           if((value < 0) || (value > 1))
              return BadValue;
	   pPriv->crtnum = value;
#ifdef SISDUALHEAD
           if(pPriv->dualHeadMode) pSiSEnt->curxvcrtnum = value;
#endif
        }
     } else return BadMatch;
  } else {
#ifdef XV_SD_DEPRECATED
     return(SISSetPortUtilAttribute(pScrn, attribute, value, pPriv));
#else
     return BadMatch;
#endif
  }
  return Success;
}

/*********************************
 *       GetPortAttribute()      *
 *********************************/

static int
SISGetPortAttribute(ScrnInfoPtr pScrn, Atom attribute,
			INT32 *value, pointer data)
{
  SISPortPrivPtr pPriv = (SISPortPrivPtr)data;
  SISPtr pSiS = SISPTR(pScrn);
#ifdef SISDUALHEAD
  SISEntPtr pSiSEnt = pSiS->entityPrivate;;
#endif

  if(attribute == pSiS->xvBrightness) {
     *value = pPriv->brightness;
  } else if(attribute == pSiS->xvContrast) {
     *value = pPriv->contrast;
  } else if(attribute == pSiS->xvColorKey) {
     *value = pPriv->colorKey;
  } else if(attribute == pSiS->xvAutopaintColorKey) {
     *value = (pPriv->autopaintColorKey) ? 1 : 0;
  } else if(attribute == pSiS->xvDisableGfx) {
     *value = (pPriv->disablegfx) ? 1 : 0;
  } else if(attribute == pSiS->xvDisableGfxLR) {
     *value = (pPriv->disablegfxlr) ? 1 : 0;
  } else if(attribute == pSiS->xvTVXPosition) {
     *value = SiS_GetTVxposoffset(pScrn);
  } else if(attribute == pSiS->xvTVYPosition) {
     *value = SiS_GetTVyposoffset(pScrn);
  } else if(attribute == pSiS->xvDisableColorkey) {
     *value = (pSiS->disablecolorkeycurrent) ? 1 : 0;
  } else if(attribute == pSiS->xvUseChromakey) {
     *value = (pPriv->usechromakey) ? 1 : 0;
  } else if(attribute == pSiS->xvInsideChromakey) {
     *value = (pPriv->insidechromakey) ? 1 : 0;
  } else if(attribute == pSiS->xvYUVChromakey) {
     *value = (pPriv->yuvchromakey) ? 1 : 0;
  } else if(attribute == pSiS->xvChromaMin) {
     *value = pPriv->chromamin;
  } else if(attribute == pSiS->xvChromaMax) {
     *value = pPriv->chromamax;
#ifdef SISDEINT
  } else if(attribute == pSiS->xvdeintmeth) {
     *value = pPriv->deinterlacemethod;
#endif
#ifdef SIS_CP
  SIS_CP_VIDEO_GETATTRIBUTE
#endif
  } else if(attribute == pSiS->xvHue) {
     if(pSiS->VGAEngine == SIS_315_VGA) {
        *value = pPriv->hue;
     } else return BadMatch;
  } else if(attribute == pSiS->xvSaturation) {
     if(pSiS->VGAEngine == SIS_315_VGA) {
        *value = pPriv->saturation;
     } else return BadMatch;
  } else if(attribute == pSiS->xvGammaRed) {
     if(pSiS->VGAEngine == SIS_315_VGA) {
        *value = pSiS->XvGammaRed;
     } else return BadMatch;
  } else if(attribute == pSiS->xvGammaGreen) {
     if(pSiS->VGAEngine == SIS_315_VGA) {
        *value = pSiS->XvGammaGreen;
     } else return BadMatch;
  } else if(attribute == pSiS->xvGammaBlue) {
     if(pSiS->VGAEngine == SIS_315_VGA) {
        *value = pSiS->XvGammaBlue;
     } else return BadMatch;
  } else if(attribute == pSiS->xvSwitchCRT) {
     if(pSiS->VGAEngine == SIS_315_VGA) {
#ifdef SISDUALHEAD
        if(pPriv->dualHeadMode)
           *value = pSiSEnt->curxvcrtnum;
        else
#endif
           *value = pPriv->crtnum;
     } else return BadMatch;
  } else {
#ifdef XV_SD_DEPRECATED
     return(SISGetPortUtilAttribute(pScrn, attribute, value, pPriv));
#else
     return BadMatch;
#endif
  }
  return Success;
}

/*********************************
 *         QueryBestSize()       *
 *********************************/

static void
SISQueryBestSize(
  ScrnInfoPtr pScrn,
  Bool motion,
  short vid_w, short vid_h,
  short drw_w, short drw_h,
  unsigned int *p_w, unsigned int *p_h,
  pointer data
){
  *p_w = drw_w;
  *p_h = drw_h;
}

/*********************************
 *       Calc scaling factor     *
 *********************************/

static void
calc_scale_factor(SISOverlayPtr pOverlay, ScrnInfoPtr pScrn,
                 SISPortPrivPtr pPriv, int index, int iscrt2)
{
  SISPtr pSiS = SISPTR(pScrn);
  CARD32 I=0,mult=0;
  int flag=0, flag2=0;

  int dstW = pOverlay->dstBox.x2 - pOverlay->dstBox.x1;
  int dstH = pOverlay->dstBox.y2 - pOverlay->dstBox.y1;
  int srcW = pOverlay->srcW;
  int srcH = pOverlay->srcH;
  CARD16 LCDheight = pSiS->LCDheight;
  int srcPitch = pOverlay->origPitch;
  int origdstH = dstH;
  int modeflags = pOverlay->currentmode->Flags;

  /* Stretch image due to panel link scaling */
  if(pSiS->VBFlags & (CRT2_LCD | CRT1_LCDA)) {
     if(pPriv->bridgeIsSlave) {
	if(pSiS->VBFlags2 & (VB2_LVDS | VB2_30xBDH)) {
	   if(pSiS->MiscFlags & MISC_PANELLINKSCALER) {
	      dstH = (dstH * LCDheight) / pOverlay->SCREENheight;
	   }
	}
     } else if((iscrt2 && (pSiS->VBFlags & CRT2_LCD)) ||
	       (!iscrt2 && (pSiS->VBFlags & CRT1_LCDA))) {
	if((pSiS->VBFlags2 & (VB2_LVDS | VB2_30xBDH)) || (pSiS->VBFlags & CRT1_LCDA)) {
	   if(pSiS->MiscFlags & MISC_PANELLINKSCALER) {
	      dstH = (dstH * LCDheight) / pOverlay->SCREENheight;
	      if(pPriv->displayMode == DISPMODE_MIRROR) flag = 1;
	   }
	}
     }
     if((pPriv->bridgeIsSlave || iscrt2) &&
        (pSiS->MiscFlags & MISC_STNMODE)) {
	flag2 = 1;
     }
  }

  /* For double scan modes, we need to double the height
   * On 315 and 550 (?), we need to double the width as well.
   * Interlace mode vice versa.
   */
  if((modeflags & V_DBLSCAN) && !flag2) {
     dstH = origdstH << 1;
     flag = 0;
     if((pSiS->ChipType >= SIS_315H) &&
	(pSiS->ChipType <= SIS_550)) {
	dstW <<= 1;
     }
  } else if(modeflags & V_INTERLACE) {
     dstH = origdstH >> 1;
     flag = 0;
  }

  pOverlay->tap_scale = 1.0;

  if(dstW < OVERLAY_MIN_WIDTH) dstW = OVERLAY_MIN_WIDTH;

  if(dstW == srcW) {

     pOverlay->HUSF   = 0x00;
     pOverlay->IntBit = 0x05;
     pOverlay->wHPre  = 0;

  } else if(dstW > srcW) {

     pOverlay->IntBit = 0x04;
     pOverlay->wHPre  = 0;

     if(pPriv->havetapscaler) {
        if((dstW > 2) && (srcW > 2)) {
           pOverlay->HUSF = (((srcW - 2) << 16) + dstW - 3) / (dstW - 2);
        } else {
           pOverlay->HUSF = ((srcW << 16) + dstW - 1) / dstW;
        }
     } else {
        dstW += 2;
        pOverlay->HUSF = (srcW << 16) / dstW;
     }

  } else {

     int tmpW = dstW;

     /* It seems, the hardware can't scale below factor .125 (=1/8) if the
        pitch isn't a multiple of 256.
	TODO: Test this on the 315 series!
      */
     if((srcPitch % 256) || (srcPitch < 256)) {
        if(((dstW * 1000) / srcW) < 125) dstW = tmpW = ((srcW * 125) / 1000) + 1;
     }

     I = 0;
     pOverlay->IntBit = 0x01;
     while(srcW >= tmpW) {
        tmpW <<= 1;
        I++;
     }
     pOverlay->wHPre = (CARD8)(I - 1);
     dstW <<= (I - 1);

     pOverlay->tap_scale = (float)srcW / (float)dstW;
     if(pOverlay->tap_scale < 1.0) pOverlay->tap_scale = 1.0;

     if((srcW % dstW))
        pOverlay->HUSF = ((srcW - dstW) << 16) / dstW;
     else
        pOverlay->HUSF = 0;
  }

  if(dstH < OVERLAY_MIN_HEIGHT) dstH = OVERLAY_MIN_HEIGHT;

  if(dstH == srcH) {

     pOverlay->VUSF   = 0x00;
     pOverlay->IntBit |= 0x0A;

  } else if(dstH > srcH) {

     dstH += 2;
     pOverlay->IntBit |= 0x08;

     if(pPriv->havetapscaler) {
        if((dstH > 2) && (srcH > 2)) {
           pOverlay->VUSF = (((srcH - 2) << 16) - 32768 + dstH - 3) / (dstH - 2);
        } else {
           pOverlay->VUSF = ((srcH << 16) + dstH - 1) / dstH;
        }
     } else {
        pOverlay->VUSF = (srcH << 16) / dstH;
     }

  } else {

     I = srcH / dstH;
     pOverlay->IntBit |= 0x02;

     if(I < 2) {
	pOverlay->VUSF = ((srcH - dstH) << 16) / dstH;
	/* Needed for LCD-scaling modes */
	if((flag) && (mult = (srcH / origdstH)) >= 2) {
	   pOverlay->pitch /= mult;
	}
     } else {
#if 0
	if(((pOverlay->bobEnable & 0x08) == 0x00) &&
	   (((srcPitch * I) >> 2) > 0xFFF)){
	   pOverlay->bobEnable |= 0x08;
	   srcPitch >>= 1;
	}
#endif
	if(((srcPitch * I) >> 2) > 0xFFF) {
	   I = (0xFFF * 2 / srcPitch);
	   pOverlay->VUSF = 0xFFFF;
	} else {
	   dstH = I * dstH;
	   if(srcH % dstH)
	      pOverlay->VUSF = ((srcH - dstH) << 16) / dstH;
	   else
	      pOverlay->VUSF = 0;
	}
	/* set video frame buffer offset */
	pOverlay->pitch = (CARD16)(srcPitch * I);
     }
  }
}

#ifdef SISMERGED
static void
calc_scale_factor_2(SISOverlayPtr pOverlay, ScrnInfoPtr pScrn,
                 SISPortPrivPtr pPriv, int index, int iscrt2)
{
  SISPtr pSiS = SISPTR(pScrn);
  CARD32 I=0,mult=0;
  int flag=0, flag2=0;

  int dstW = pOverlay->dstBox2.x2 - pOverlay->dstBox2.x1;
  int dstH = pOverlay->dstBox2.y2 - pOverlay->dstBox2.y1;
  int srcW = pOverlay->srcW2;
  int srcH = pOverlay->srcH2;
  CARD16 LCDheight = pSiS->LCDheight;
  int srcPitch = pOverlay->origPitch;
  int origdstH = dstH;
  int modeflags = pOverlay->currentmode2->Flags;

  /* Stretch image due to panel link scaling */
  if(pSiS->VBFlags & CRT2_LCD) {
     if(pSiS->VBFlags2 & (VB2_LVDS | VB2_30xBDH)) {
	if(pSiS->MiscFlags & MISC_PANELLINKSCALER) {
	   dstH = (dstH * LCDheight) / pOverlay->SCREENheight2;
	   flag = 1;
	}
	if(pSiS->MiscFlags & MISC_STNMODE) flag2 = 1;
     }
  }
  /* For double scan modes, we need to double the height
   * On 315 and 550 (?), we need to double the width as well.
   * Interlace mode vice versa.
   */
  if((modeflags & V_DBLSCAN) && !flag2) {
     dstH = origdstH << 1;
     flag = 0;
     if((pSiS->ChipType >= SIS_315H) &&
	(pSiS->ChipType <= SIS_550)) {
	dstW <<= 1;
     }
  }
  if(modeflags & V_INTERLACE) {
     dstH = origdstH >> 1;
     flag = 0;
  }

  pOverlay->tap_scale2 = 1.0;

  if(dstW < OVERLAY_MIN_WIDTH) dstW = OVERLAY_MIN_WIDTH;

  if(dstW == srcW) {

     pOverlay->HUSF2   = 0x00;
     pOverlay->IntBit2 = 0x05;
     pOverlay->wHPre2  = 0;

  } else if(dstW > srcW) {

     pOverlay->IntBit2 = 0x04;
     pOverlay->wHPre2  = 0;

     if(pPriv->havetapscaler) {
        if((dstW > 2) && (srcW > 2)) {
           pOverlay->HUSF2 = (((srcW - 2) << 16) + dstW - 3) / (dstW - 2);
        } else {
           pOverlay->HUSF2 = ((srcW << 16) + dstW - 1) / dstW;
        }
     } else {
        dstW += 2;
        pOverlay->HUSF2 = (srcW << 16) / dstW;
     }

  } else {

     int tmpW = dstW;

     /* It seems, the hardware can't scale below factor .125 (=1/8) if the
	pitch isn't a multiple of 256.
	TODO: Test this on the 315 series!
      */
     if((srcPitch % 256) || (srcPitch < 256)) {
	if(((dstW * 1000) / srcW) < 125) dstW = tmpW = ((srcW * 125) / 1000) + 1;
     }

     I = 0;
     pOverlay->IntBit2 = 0x01;
     while(srcW >= tmpW) {
        tmpW <<= 1;
        I++;
     }
     pOverlay->wHPre2 = (CARD8)(I - 1);
     dstW <<= (I - 1);

     pOverlay->tap_scale2 = (float)srcW / (float)dstW;
     if(pOverlay->tap_scale2 < 1.0) pOverlay->tap_scale2 = 1.0;

     if((srcW % dstW))
        pOverlay->HUSF2 = ((srcW - dstW) << 16) / dstW;
     else
        pOverlay->HUSF2 = 0x00;
  }

  if(dstH < OVERLAY_MIN_HEIGHT) dstH = OVERLAY_MIN_HEIGHT;

  if(dstH == srcH) {

     pOverlay->VUSF2   = 0x00;
     pOverlay->IntBit2 |= 0x0A;

  } else if(dstH > srcH) {

     dstH += 2;
     pOverlay->IntBit2 |= 0x08;

     if(pPriv->havetapscaler) {
        if((dstH > 2) && (srcH > 2)) {
           pOverlay->VUSF2 = (((srcH - 2) << 16) - 32768 + dstH - 3) / (dstH - 2);
        } else {
           pOverlay->VUSF2 = ((srcH << 16) + dstH - 1) / dstH;
        }
     } else {
        pOverlay->VUSF2 = (srcH << 16) / dstH;
     }

  } else {

     I = srcH / dstH;
     pOverlay->IntBit2 |= 0x02;

     if(I < 2) {
	pOverlay->VUSF2 = ((srcH - dstH) << 16) / dstH;
	/* Needed for LCD-scaling modes */
	if(flag && ((mult = (srcH / origdstH)) >= 2)) {
	   pOverlay->pitch2 /= mult;
	}
     } else {
#if 0
	if(((pOverlay->bobEnable & 0x08) == 0x00) &&
	   (((srcPitch * I)>>2) > 0xFFF)){
	   pOverlay->bobEnable |= 0x08;
	   srcPitch >>= 1;
	}
#endif
	if(((srcPitch * I) >> 2) > 0xFFF) {
	   I = (0xFFF * 2 / srcPitch);
	   pOverlay->VUSF2 = 0xFFFF;
	} else {
	   dstH = I * dstH;
	   if(srcH % dstH)
	      pOverlay->VUSF2 = ((srcH - dstH) << 16) / dstH;
	   else
	      pOverlay->VUSF2 = 0x00;
	}
	/* set video frame buffer offset */
	pOverlay->pitch2 = (CARD16)(srcPitch * I);
     }
  }
}
#endif

/*********************************
 *    Handle 4-tap scaler (340)  *
 *********************************/

static float
tap_dda_func(float x)
{
    double pi = 3.14159265358979;
    float  r = 0.5, y;

    if(x == 0.0) {
       y = 1.0;
    } else if(x == -1.0 || x == 1.0) {
       y = 0.0;
       /* case ((x == -1.0 / (r * 2.0)) || (x == 1.0 / (r * 2.0))): */
       /* y = (float)(r / 2.0 * sin(pi / (2.0 * r))); = 0.013700916287197;    */
    } else {
       y = sin(pi * x) / (pi * x) * cos(r * pi * x) / (1 - x * x);
       /* y = sin(pi * x) / (pi * x) * cos(r * pi * x) / (1 - 4 * r * r * x * x); */
    }

    return y;
}

static void
set_dda_regs(SISPtr pSiS, float scale)
{
    float W[4], WS, myadd;
    int   *temp[4], *wm1, *wm2, *wm3, *wm4;
    int   i, j, w, tidx, weightmatrix[16][4];

    for(i = 0; i < 16; i++) {

       myadd = ((float)i) / 16.0;
       WS = W[0] = tap_dda_func((myadd + 1.0) / scale);
       W[1] = tap_dda_func(myadd / scale);
       WS += W[1];
       W[2] = tap_dda_func((myadd - 1.0) / scale);
       WS += W[2];
       W[3] = tap_dda_func((myadd - 2.0) / scale);
       WS += W[3];

       w = 0;
       for(j = 0; j < 4; j++) {
	  weightmatrix[i][j] = (int)(((float)((W[j] * 16.0 / WS) + 0.5)));
	  w += weightmatrix[i][j];
       }

       if(w == 12) {

	  weightmatrix[i][0]++;
	  weightmatrix[i][1]++;
	  weightmatrix[i][2]++;
	  weightmatrix[i][3]++;

       } else if(w == 20) {

	  weightmatrix[i][0]--;
	  weightmatrix[i][1]--;
	  weightmatrix[i][2]--;
	  weightmatrix[i][3]--;

       } else if(w != 16) {

	  tidx = (weightmatrix[i][0] > weightmatrix[i][1]) ? 0 : 1;
	  temp[0] = &weightmatrix[i][tidx];
	  temp[1] = &weightmatrix[i][tidx ^ 1];

	  tidx = (weightmatrix[i][2] > weightmatrix[i][3]) ? 2 : 3;
	  temp[2] = &weightmatrix[i][tidx];
	  temp[3] = &weightmatrix[i][tidx ^ 1];

	  tidx = (*(temp[0]) > *(temp[2])) ? 0 : 2;
	  wm1 = temp[tidx];
	  wm2 = temp[tidx ^ 2];

	  tidx = (*(temp[1]) > *(temp[3])) ? 1 : 3;
	  wm3 = temp[tidx];
	  wm4 = temp[tidx ^ 2];

	  switch(w) {
	     case 13:
		(*wm1)++;
		(*wm4)++;
		if(*wm2 > *wm3) (*wm2)++;
		else            (*wm3)++;
		break;
	     case 14:
		(*wm1)++;
		(*wm4)++;
		break;
	     case 15:
		(*wm1)++;
		break;
	     case 17:
		(*wm4)--;
		break;
	     case 18:
		(*wm1)--;
		(*wm4)--;
		break;
	     case 19:
		(*wm1)--;
		(*wm4)--;
		if(*wm2 > *wm3) (*wm3)--;
		else            (*wm2)--;
	  }
       }
    }

    /* Set 4-tap scaler video regs 0x75-0xb4 */
    w = 0x75;
    for(i = 0; i < 16; i++) {
       for(j = 0; j < 4; j++, w++) {
          setvideoregmask(pSiS, w, weightmatrix[i][j], 0x3f);
       }
    }
}

/*********************************
 *     Calc line buffer size     *
 *********************************/

static CARD16
calc_line_buf_size(CARD32 srcW, CARD8 wHPre, CARD8 planar, SISPortPrivPtr pPriv)
{
    CARD32 I, mask = 0xffffffff, shift = pPriv->is761 ? 1 : 0;

    if(planar) {

	switch(wHPre & 0x07) {
	    case 3:
		shift += 8;
		mask <<= shift;
		I = srcW >> shift;
		if((mask & srcW) != srcW) I++;
		I <<= 5;
		break;
	    case 4:
		shift += 9;
		mask <<= shift;
		I = srcW >> shift;
		if((mask & srcW) != srcW) I++;
		I <<= 6;
		break;
	    case 5:
		shift += 10;
		mask <<= shift;
		I = srcW >> shift;
		if((mask & srcW) != srcW) I++;
		I <<= 7;
		break;
	    case 6:
		if(pPriv->is340 || pPriv->isXGI || pPriv->is761) {
		   shift += 11;
		   mask <<= shift;
		   I = srcW >> shift;
		   if((mask & srcW) != srcW) I++;
		   I <<= 8;
		   break;
		} else {
		   return((CARD16)(255));
		}
	    default:
		shift += 7;
		mask <<= shift;
		I = srcW >> shift;
		if((mask & srcW) != srcW) I++;
		I <<= 4;
		break;
	}

    } else { /* packed */

	shift += 3;
	mask <<= shift;
	I = srcW >> shift;
	if((mask & srcW) != srcW) I++;

    }

    if(I <= 3) I = 4;

    return((CARD16)(I - 1));
}

static __inline void
calc_line_buf_size_1(SISOverlayPtr pOverlay, SISPortPrivPtr pPriv)
{
    pOverlay->lineBufSize =
	calc_line_buf_size(pOverlay->srcW, pOverlay->wHPre, pOverlay->planar, pPriv);
}

#ifdef SISMERGED
static __inline void
calc_line_buf_size_2(SISOverlayPtr pOverlay, SISPortPrivPtr pPriv)
{
    pOverlay->lineBufSize2 =
	calc_line_buf_size(pOverlay->srcW2, pOverlay->wHPre2, pOverlay->planar, pPriv);
}

/**********************************
 *En/Disable merging of linebuffer*
 **********************************/

static void
merge_line_buf_mfb(SISPtr pSiS, SISPortPrivPtr pPriv, Bool enable1, Bool enable2,
                   short width1, short width2, short limit)
{
    UChar misc1, misc2, mask = pPriv->linebufmask;

    if(pPriv->hasTwoOverlays) {     /* This means we are in MIRROR mode */

       misc2 = 0x00;
       if(enable1) misc1 = 0x04;
       else 	   misc1 = 0x00;
       setvideoregmask(pSiS, Index_VI_Control_Misc2, misc2, mask);
       setvideoregmask(pSiS, Index_VI_Control_Misc1, misc1, 0x04);

       misc2 = 0x01;
       if(enable2) misc1 = 0x04;
       else        misc1 = 0x00;
       setvideoregmask(pSiS, Index_VI_Control_Misc2, misc2, mask);
       setvideoregmask(pSiS, Index_VI_Control_Misc1, misc1, 0x04);

    } else {			/* This means we are either in SINGLE1 or SINGLE2 mode */

       misc2 = 0x00;
       if(enable1 || enable2) {
          if(pSiS->MiscFlags & MISC_SIS760ONEOVERLAY) {
	     if((width1 > (limit * 2)) || (width2 > (limit * 2))) {
		 misc2 = 0x20;
	      } else {
		 misc2 = 0x10;
	      }
	      misc1 = 0x00;
	  } else {
             misc1 = 0x04;
	  }
       } else {
          misc1 = 0x00;
       }

       setvideoregmask(pSiS, Index_VI_Control_Misc2, misc2, mask);
       setvideoregmask(pSiS, Index_VI_Control_Misc1, misc1, 0x04);

    }
}
#endif

/* About linebuffer merging:
 *
 * For example the 651:
 * Each overlay has 4 line buffers, 384 bytes each (<-- Is that really correct? 1920 / 384 = 5 !!!)
 * If the source width is greater than 384, line buffers must be merged.
 * Dual merge: Only O1 usable (uses overlay 2's linebuffer), maximum width 384*2
 * Individual merge: Both overlays available, maximum width 384*2
 * All merge: Only overlay 1 available, maximum width = 384*4 (<--- should be 1920, is 1536...)
 *
 *
 *        Normally:                  Dual merge:                 Individual merge
 *  Overlay 1    Overlay 2         Overlay 1 only!                Both overlays
 *  ___1___      ___5___           ___1___ ___2___ -\         O1  ___1___ ___2___
 *  ___2___      ___6___           ___3___ ___4___   \_ O 1   O1  ___3___ ___4___
 *  ___3___      ___7___	   ___5___ ___6___   /        O2  ___5___ ___6___
 *  ___4___      ___8___           ___7___ ___8___ -/         O2  ___7___ ___8___
 *
 *
 * All merge:          ___1___ ___2___ ___3___ ___4___
 * (Overlay 1 only!)   ___5___ ___6___ ___7___ ___8___
 *
 * Individual merge is supported on all chipsets.
 * Dual merge is only supported on the 300 series and M650/651 and later.
 * All merge is only supported on the M650/651 and later.
 * Single-Overlay-chipsets only support Individual merge.
 *
 */

static void
merge_line_buf(SISPtr pSiS, SISPortPrivPtr pPriv, Bool enable, short width, short limit)
{
  UChar misc1, misc2, mask = pPriv->linebufmask;

  if(enable) { 		/* ----- enable linebuffer merge */

    switch(pPriv->displayMode){
    case DISPMODE_SINGLE1:
        if(pSiS->VGAEngine == SIS_300_VGA) {
           if(pPriv->dualHeadMode) {
	       misc2 = 0x00;
	       misc1 = 0x04;
	   } else {
	       misc2 = 0x10;
	       misc1 = 0x00;
	   }
        } else {
	   if(pPriv->hasTwoOverlays) {
	      if(pPriv->dualHeadMode) {
		 misc2 = 0x00;
		 misc1 = 0x04;
	      } else {
		 if(width > (limit * 2)) {
		    misc2 = 0x20;
		 } else {
		    misc2 = 0x10;
		 }
		 misc1 = 0x00;
	      }
	   } else if(pSiS->MiscFlags & MISC_SIS760ONEOVERLAY) {
	      if(width > (limit * 2)) {
		 misc2 = 0x20;
	      } else {
		 misc2 = 0x10;
	      }
	      misc1 = 0x00;
	   } else {
	      misc2 = 0x00;
	      misc1 = 0x04;
	   }
	}
	setvideoregmask(pSiS, Index_VI_Control_Misc2, misc2, mask);
	setvideoregmask(pSiS, Index_VI_Control_Misc1, misc1, 0x04);
      	break;

    case DISPMODE_SINGLE2:
        if(pSiS->VGAEngine == SIS_300_VGA) {
	   if(pPriv->dualHeadMode) {
	      misc2 = 0x01;
	      misc1 = 0x04;
	   } else {
	      misc2 = 0x10;
	      misc1 = 0x00;
	   }
	} else {
	   if(pPriv->hasTwoOverlays) {
	      if(pPriv->dualHeadMode) {
		 misc2 = 0x01;
		 misc1 = 0x04;
	      } else {
		 if(width > (limit * 2)) {
		    misc2 = 0x20;
		 } else {
		    misc2 = 0x10;
		 }
		 misc1 = 0x00;
	      }
	   } else if(pSiS->MiscFlags & MISC_SIS760ONEOVERLAY) {
	      if(width > (limit * 2)) {
		 misc2 = 0x20;
	      } else {
		 misc2 = 0x10;
	      }
	      misc1 = 0x00;
	   } else {
	      misc2 = 0x00;
	      misc1 = 0x04;
	   }
	}
	setvideoregmask(pSiS, Index_VI_Control_Misc2, misc2, mask);
	setvideoregmask(pSiS, Index_VI_Control_Misc1, misc1, 0x04);
     	break;

    case DISPMODE_MIRROR:   /* This can only be on chips with 2 overlays */
    default:
        setvideoregmask(pSiS, Index_VI_Control_Misc2, 0x00, mask);
      	setvideoregmask(pSiS, Index_VI_Control_Misc1, 0x04, 0x04);
	setvideoregmask(pSiS, Index_VI_Control_Misc2, 0x01, mask);
      	setvideoregmask(pSiS, Index_VI_Control_Misc1, 0x04, 0x04);
        break;
    }

  } else {		/* ----- disable linebuffer merge */

    switch(pPriv->displayMode) {

    case DISPMODE_SINGLE1:
	setvideoregmask(pSiS, Index_VI_Control_Misc2, 0x00, mask);
	setvideoregmask(pSiS, Index_VI_Control_Misc1, 0x00, 0x04);
	break;

    case DISPMODE_SINGLE2:
	if(pSiS->VGAEngine == SIS_300_VGA) {
	   if(pPriv->dualHeadMode) misc2 = 0x01;
	   else       		   misc2 = 0x00;
	} else {
	   if(pPriv->hasTwoOverlays) {
	      if(pPriv->dualHeadMode) misc2 = 0x01;
	      else                    misc2 = 0x00;
	   } else {
	      misc2 = 0x00;
	   }
	}
	setvideoregmask(pSiS, Index_VI_Control_Misc2, misc2, mask);
	setvideoregmask(pSiS, Index_VI_Control_Misc1, 0x00, 0x04);
	break;

    case DISPMODE_MIRROR:   /* This can only be on chips with 2 overlays */
    default:
	setvideoregmask(pSiS, Index_VI_Control_Misc2, 0x00, mask);
	setvideoregmask(pSiS, Index_VI_Control_Misc1, 0x00, 0x04);
	setvideoregmask(pSiS, Index_VI_Control_Misc2, 0x01, mask);
	setvideoregmask(pSiS, Index_VI_Control_Misc1, 0x00, 0x04);
	break;
    }
  }
}

/*********************************
 *      Select video format      *
 *********************************/

static __inline void
set_format(SISPtr pSiS, SISOverlayPtr pOverlay)
{
    CARD8 fmt;

    switch (pOverlay->pixelFormat){
    case PIXEL_FMT_YV12:
    case PIXEL_FMT_I420:
	fmt = 0x0c;
	break;
    case PIXEL_FMT_YUY2:
	fmt = 0x28;
	break;
    case PIXEL_FMT_UYVY:
	fmt = 0x08;
	break;
    case PIXEL_FMT_YVYU:
	fmt = 0x38;
	break;
    case PIXEL_FMT_NV12:
	fmt = 0x4c;
	break;
    case PIXEL_FMT_NV21:
	fmt = 0x5c;
	break;
    case PIXEL_FMT_RGB5:   /* D[5:4] : 00 RGB555, 01 RGB 565 */
	fmt = 0x00;
	break;
    case PIXEL_FMT_RGB6:
	fmt = 0x10;
	break;
    default:
	fmt = 0x00;
    }
    setvideoregmask(pSiS, Index_VI_Control_Misc0, fmt, 0xfc);
}

/*********************************
 *  Set various video registers  *
 *********************************/

static __inline void
set_colorkey(SISPtr pSiS, CARD32 colorkey)
{
    CARD8 r, g, b;

    b = (CARD8)(colorkey & 0xFF);
    g = (CARD8)((colorkey >> 8) & 0xFF);
    r = (CARD8)((colorkey >> 16) & 0xFF);

    setvideoreg(pSiS, Index_VI_Overlay_ColorKey_Blue_Min  ,(CARD8)b);
    setvideoreg(pSiS, Index_VI_Overlay_ColorKey_Green_Min ,(CARD8)g);
    setvideoreg(pSiS, Index_VI_Overlay_ColorKey_Red_Min   ,(CARD8)r);

    setvideoreg(pSiS, Index_VI_Overlay_ColorKey_Blue_Max  ,(CARD8)b);
    setvideoreg(pSiS, Index_VI_Overlay_ColorKey_Green_Max ,(CARD8)g);
    setvideoreg(pSiS, Index_VI_Overlay_ColorKey_Red_Max   ,(CARD8)r);
}

static __inline void
set_chromakey(SISPtr pSiS, CARD32 chromamin, CARD32 chromamax)
{
    CARD8 r1, g1, b1;
    CARD8 r2, g2, b2;

    b1 = (CARD8)(chromamin & 0xFF);
    g1 = (CARD8)((chromamin >> 8) & 0xFF);
    r1 = (CARD8)((chromamin >> 16) & 0xFF);
    b2 = (CARD8)(chromamax & 0xFF);
    g2 = (CARD8)((chromamax >> 8) & 0xFF);
    r2 = (CARD8)((chromamax >> 16) & 0xFF);

    setvideoreg(pSiS, Index_VI_Overlay_ChromaKey_Blue_V_Min  ,(CARD8)b1);
    setvideoreg(pSiS, Index_VI_Overlay_ChromaKey_Green_U_Min ,(CARD8)g1);
    setvideoreg(pSiS, Index_VI_Overlay_ChromaKey_Red_Y_Min   ,(CARD8)r1);

    setvideoreg(pSiS, Index_VI_Overlay_ChromaKey_Blue_V_Max  ,(CARD8)b2);
    setvideoreg(pSiS, Index_VI_Overlay_ChromaKey_Green_U_Max ,(CARD8)g2);
    setvideoreg(pSiS, Index_VI_Overlay_ChromaKey_Red_Y_Max   ,(CARD8)r2);
}

static __inline void
set_brightness(SISPtr pSiS, CARD8 brightness)
{
    setvideoreg(pSiS, Index_VI_Brightness, brightness);
}

static __inline void
set_contrast(SISPtr pSiS, CARD8 contrast)
{
    setvideoregmask(pSiS, Index_VI_Contrast_Enh_Ctrl, contrast, 0x07);
}

/* 315 series and later only */
static __inline void
set_saturation(SISPtr pSiS, short saturation)
{
    CARD8 temp = 0;

    if(saturation < 0) {
    	temp |= 0x88;
	saturation = -saturation;
    }
    temp |= (saturation & 0x07);
    temp |= ((saturation & 0x07) << 4);

    setvideoreg(pSiS, Index_VI_Saturation, temp);
}

/* 315 series and later only */
static __inline void
set_hue(SISPtr pSiS, CARD8 hue)
{
    setvideoregmask(pSiS, Index_VI_Hue, (hue & 0x08) ? (hue ^ 0x07) : hue, 0x0F);
}

static __inline void
set_disablegfx(SISPtr pSiS, Bool mybool, SISOverlayPtr pOverlay)
{
    /* This is not supported on M65x, 65x (x>0) or later */
    /* For CRT1 ONLY!!! */
    if((!(pSiS->ChipFlags & SiSCF_Is65x)) &&
       (pSiS->Chipset != PCI_CHIP_SIS660) &&
       (pSiS->Chipset != PCI_CHIP_SIS340) &&
       (pSiS->Chipset != PCI_CHIP_XGIXG20) &&
       (pSiS->Chipset != PCI_CHIP_XGIXG40)) {
       setvideoregmask(pSiS, Index_VI_Control_Misc2, mybool ? 0x04 : 0x00, 0x04);
       if(mybool) pOverlay->keyOP = VI_ROP_Always;
    }
}

static __inline void
set_disablegfxlr(SISPtr pSiS, Bool mybool, SISOverlayPtr pOverlay)
{
    setvideoregmask(pSiS, Index_VI_Control_Misc1, mybool ? 0x01 : 0x00, 0x01);
    if(mybool) pOverlay->keyOP = VI_ROP_Always;
}

#ifdef SIS_CP
    SIS_CP_VIDEO_SUBS
#endif

/*********************************
 *   Set main overlay registers  *
 *********************************/

static void
set_overlay(SISPtr pSiS, SISOverlayPtr pOverlay, SISPortPrivPtr pPriv, int index, int iscrt2)
{
    CARD8  h_over, v_over;
    CARD16 top, bottom, left, right, pitch = 0;
    CARD16 screenX, screenY;
    CARD32 PSY;
    int    modeflags, totalPixels, confactor, sample, watchdog = 0;

#ifdef SISMERGED
    if(pSiS->MergedFB && iscrt2) {
       screenX = pOverlay->currentmode2->HDisplay;
       screenY = pOverlay->currentmode2->VDisplay;
       modeflags = pOverlay->currentmode2->Flags;
       top = pOverlay->dstBox2.y1;
       bottom = pOverlay->dstBox2.y2;
       left = pOverlay->dstBox2.x1;
       right = pOverlay->dstBox2.x2;
       pitch = pOverlay->pitch2 >> pPriv->shiftValue;
    } else {
#endif
       screenX = pOverlay->currentmode->HDisplay;
       screenY = pOverlay->currentmode->VDisplay;
       modeflags = pOverlay->currentmode->Flags;
       top = pOverlay->dstBox.y1;
       bottom = pOverlay->dstBox.y2;
       left = pOverlay->dstBox.x1;
       right = pOverlay->dstBox.x2;
       pitch = pOverlay->pitch >> pPriv->shiftValue;
#ifdef SISMERGED
    }
#endif

    if(bottom > screenY) bottom = screenY;
    if(right  > screenX) right  = screenX;

    /* calculate contrast factor */
    totalPixels = (right - left) * (bottom - top);
    confactor = (totalPixels - 10000) / 20000;
    if(confactor > 3) confactor = 3;
    switch(confactor) {
    case 1:  sample = 4096 << 10; break;
    case 2:
    case 3:  sample = 8192 << 10; break;
    default: sample = 2048 << 10;
    }
    sample /= totalPixels;
    confactor <<= 6;

    /* Correct coordinates for doublescan/interlace modes */
    if( (modeflags & V_DBLSCAN) &&
        (!((pPriv->bridgeIsSlave || iscrt2) && (pSiS->MiscFlags & MISC_STNMODE))) ) {
       /* DoubleScan modes require Y coordinates * 2 */
       top <<= 1;
       bottom <<= 1;
    } else if(modeflags & V_INTERLACE) {
       /* Interlace modes require Y coordinates / 2 */
       top >>= 1;
       bottom >>= 1;
    }

    h_over = (((left >> 8) & 0x0f) | ((right >> 4) & 0xf0));
    v_over = (((top >> 8) & 0x0f) | ((bottom >> 4) & 0xf0));

    /* set line buffer size */
#ifdef SISMERGED
    if(pSiS->MergedFB && iscrt2) {
       setvideoreg(pSiS, Index_VI_Line_Buffer_Size, (CARD8)pOverlay->lineBufSize2);
       if(pPriv->is340 || pPriv->is761 || pPriv->isXGI) {
          setvideoreg(pSiS, Index_VI_Line_Buffer_Size_High, (CARD8)(pOverlay->lineBufSize2 >> 8));
       }
    } else {
#endif
       setvideoreg(pSiS, Index_VI_Line_Buffer_Size, (CARD8)pOverlay->lineBufSize);
       if(pPriv->is340 || pPriv->is761 || pPriv->isXGI) {
          setvideoreg(pSiS, Index_VI_Line_Buffer_Size_High, (CARD8)(pOverlay->lineBufSize >> 8));
       }
#ifdef SISMERGED
    }
#endif

    /* set color key mode */
    setvideoregmask(pSiS, Index_VI_Key_Overlay_OP, pOverlay->keyOP, 0x0f);

    /* We don't have to wait for vertical retrace in all cases */
    if(pPriv->mustwait) {
       if(pSiS->VGAEngine == SIS_315_VGA) {

          if(index) {
	     CARD16 mytop = getvideoreg(pSiS, Index_VI_Win_Ver_Disp_Start_Low);
	     mytop |= ((getvideoreg(pSiS, Index_VI_Win_Ver_Over) & 0x0f) << 8);
	     pOverlay->oldtop = mytop;
	     watchdog = 0xffff;
	     if(mytop < screenY - 2) {
		do {
		   watchdog = get_scanline_CRT2(pSiS, pPriv);
		} while((watchdog <= mytop) || (watchdog >= screenY));
	     }
	     pOverlay->oldLine = watchdog;
	  }

       } else {

	  watchdog = WATCHDOG_DELAY;
	  while(pOverlay->VBlankActiveFunc(pSiS, pPriv) && --watchdog);
	  watchdog = WATCHDOG_DELAY;
	  while((!pOverlay->VBlankActiveFunc(pSiS, pPriv)) && --watchdog);

       }
    }

    /* Unlock address registers */
    setvideoregmask(pSiS, Index_VI_Control_Misc1, 0x20, 0x20);

    /* Set destination window position */
    setvideoreg(pSiS, Index_VI_Win_Hor_Disp_Start_Low, (CARD8)left);
    setvideoreg(pSiS, Index_VI_Win_Hor_Disp_End_Low,   (CARD8)right);
    setvideoreg(pSiS, Index_VI_Win_Hor_Over,           (CARD8)h_over);

    setvideoreg(pSiS, Index_VI_Win_Ver_Disp_Start_Low, (CARD8)top);
    setvideoreg(pSiS, Index_VI_Win_Ver_Disp_End_Low,   (CARD8)bottom);
    setvideoreg(pSiS, Index_VI_Win_Ver_Over,           (CARD8)v_over);

    /* Contrast factor */
    setvideoregmask(pSiS, Index_VI_Contrast_Enh_Ctrl,  (CARD8)confactor, 0xc0);
    setvideoreg(pSiS, Index_VI_Contrast_Factor,        sample);

    /* Set Y buf pitch */
    setvideoreg(pSiS, Index_VI_Disp_Y_Buf_Pitch_Low, (CARD8)(pitch));
    setvideoregmask(pSiS, Index_VI_Disp_Y_UV_Buf_Pitch_Middle, (CARD8)(pitch >> 8), 0x0f);

    /* Set Y start address */
#ifdef SISMERGED
    if(pSiS->MergedFB && iscrt2) {
       PSY = pOverlay->PSY2;
    } else
#endif
       PSY = pOverlay->PSY;

    setvideoreg(pSiS, Index_VI_Disp_Y_Buf_Start_Low,    (CARD8)(PSY));
    setvideoreg(pSiS, Index_VI_Disp_Y_Buf_Start_Middle, (CARD8)(PSY >> 8));
    setvideoreg(pSiS, Index_VI_Disp_Y_Buf_Start_High,   (CARD8)(PSY >> 16));

    /* Set 315 series overflow bits for Y plane */
    if(pSiS->VGAEngine == SIS_315_VGA) {
       setvideoreg(pSiS, Index_VI_Disp_Y_Buf_Pitch_High, (CARD8)(pitch >> 12));
       setvideoreg(pSiS, Index_VI_Y_Buf_Start_Over, ((CARD8)(PSY >> 24) & 0x03));
    }

    /* Set U/V data if using planar formats */
    if(pOverlay->planar) {

	CARD32  PSU = pOverlay->PSU;
	CARD32  PSV = pOverlay->PSV;

#ifdef SISMERGED
	if(pSiS->MergedFB && iscrt2) {
	   PSU = pOverlay->PSU2;
	   PSV = pOverlay->PSV2;
	}
#endif

	if(pOverlay->planar_shiftpitch) pitch >>= 1;

	/* Set U/V pitch */
	setvideoreg(pSiS, Index_VI_Disp_UV_Buf_Pitch_Low, (CARD8)pitch);
	setvideoregmask(pSiS, Index_VI_Disp_Y_UV_Buf_Pitch_Middle, (CARD8)(pitch >> 4), 0xf0);

	/* Set U/V start address */
	setvideoreg(pSiS, Index_VI_U_Buf_Start_Low,   (CARD8)PSU);
	setvideoreg(pSiS, Index_VI_U_Buf_Start_Middle,(CARD8)(PSU >> 8));
	setvideoreg(pSiS, Index_VI_U_Buf_Start_High,  (CARD8)(PSU >> 16));

	setvideoreg(pSiS, Index_VI_V_Buf_Start_Low,   (CARD8)PSV);
	setvideoreg(pSiS, Index_VI_V_Buf_Start_Middle,(CARD8)(PSV >> 8));
	setvideoreg(pSiS, Index_VI_V_Buf_Start_High,  (CARD8)(PSV >> 16));

	/* 315 series overflow bits */
	if(pSiS->VGAEngine == SIS_315_VGA) {
	   setvideoreg(pSiS, Index_VI_Disp_UV_Buf_Pitch_High, (CARD8)(pitch >> 12));
	   setvideoreg(pSiS, Index_VI_U_Buf_Start_Over, ((CARD8)(PSU >> 24) & 0x03));
	   if(pPriv->is661741760) {
	      setvideoregmask(pSiS, Index_VI_V_Buf_Start_Over, ((CARD8)(PSV >> 24) & 0x03), 0xc3);
	   } else {
	      setvideoreg(pSiS, Index_VI_V_Buf_Start_Over, ((CARD8)(PSV >> 24) & 0x03));
	   }
	}
    }

    setvideoregmask(pSiS, Index_VI_Control_Misc1, pOverlay->bobEnable, 0x1a);

    /* Lock the address registers */
    setvideoregmask(pSiS, Index_VI_Control_Misc1, 0x00, 0x20);

    /* Set scale factor */
#ifdef SISMERGED
    if(pSiS->MergedFB && iscrt2) {
       setvideoreg(pSiS, Index_VI_Hor_Post_Up_Scale_Low, (CARD8)(pOverlay->HUSF2));
       setvideoreg(pSiS, Index_VI_Hor_Post_Up_Scale_High,(CARD8)((pOverlay->HUSF2) >> 8));
       setvideoreg(pSiS, Index_VI_Ver_Up_Scale_Low,      (CARD8)(pOverlay->VUSF2));
       setvideoreg(pSiS, Index_VI_Ver_Up_Scale_High,     (CARD8)((pOverlay->VUSF2) >> 8));

       setvideoregmask(pSiS, Index_VI_Scale_Control,     (pOverlay->IntBit2 << 3) |
                                                         (pOverlay->wHPre2), 0x7f);

       if(pPriv->havetapscaler) {
	  if((pOverlay->tap_scale2 != pOverlay->tap_scale2_old) || pPriv->mustresettap2) {
	     set_dda_regs(pSiS, pOverlay->tap_scale2);
	     pOverlay->tap_scale2_old = pOverlay->tap_scale2;
	     pPriv->mustresettap2 = FALSE;
	  }
       }
    } else {
#endif
       setvideoreg(pSiS, Index_VI_Hor_Post_Up_Scale_Low, (CARD8)(pOverlay->HUSF));
       setvideoreg(pSiS, Index_VI_Hor_Post_Up_Scale_High,(CARD8)((pOverlay->HUSF) >> 8));
       setvideoreg(pSiS, Index_VI_Ver_Up_Scale_Low,      (CARD8)(pOverlay->VUSF));
       setvideoreg(pSiS, Index_VI_Ver_Up_Scale_High,     (CARD8)((pOverlay->VUSF) >> 8));

       setvideoregmask(pSiS, Index_VI_Scale_Control,     (pOverlay->IntBit << 3) |
                                                         (pOverlay->wHPre), 0x7f);
       if(pPriv->havetapscaler) {
	  if((pOverlay->tap_scale != pOverlay->tap_scale_old) || pPriv->mustresettap) {
	     set_dda_regs(pSiS, pOverlay->tap_scale);
	     pOverlay->tap_scale_old = pOverlay->tap_scale;
	     pPriv->mustresettap = FALSE;
	  }
       }
#ifdef SISMERGED
    }
#endif

}

/*********************************
 *       Shut down overlay       *
 *********************************/

/* Overlay MUST NOT be switched off while beam is over it */
static void
close_overlay(SISPtr pSiS, SISPortPrivPtr pPriv)
{
  int watchdog;

  if(!pPriv->overlayStatus) return;

  pPriv->overlayStatus = FALSE;

  pPriv->mustresettap = TRUE;
#ifdef SISMERGED
  pPriv->mustresettap2 = TRUE;
#endif

  if(pPriv->displayMode & (DISPMODE_MIRROR | DISPMODE_SINGLE2)) {

     /* CRT2: MIRROR or SINGLE2
      * 1 overlay:  Uses overlay 0
      * 2 overlays: Uses Overlay 1 if MIRROR or DUAL HEAD
      *             Uses Overlay 0 if SINGLE2 and not DUAL HEAD
      */

     if(pPriv->hasTwoOverlays) {

	if((pPriv->dualHeadMode) || (pPriv->displayMode == DISPMODE_MIRROR)) {
	   setvideoregmask(pSiS, Index_VI_Control_Misc2, 0x01, 0x01);
	} else {
	   setvideoregmask(pSiS, Index_VI_Control_Misc2, 0x00, 0x01);
	}

     } else if(pPriv->displayMode == DISPMODE_SINGLE2) {

#ifdef SISDUALHEAD
	if(pPriv->dualHeadMode) {
	   /* Check if overlay already grabbed by other head */
	   if(!(getsrreg(pSiS, 0x06) & 0x40)) return;
	}
#endif
	setvideoregmask(pSiS, Index_VI_Control_Misc2, 0x00, 0x01);

     }

     setvideoregmask(pSiS, Index_VI_Control_Misc1, 0x00, 0x01);

     watchdog = WATCHDOG_DELAY;
     while((!vblank_active_CRT2(pSiS, pPriv)) && --watchdog);
     watchdog = WATCHDOG_DELAY;
     while(vblank_active_CRT2(pSiS, pPriv) && --watchdog);
     setvideoregmask(pSiS, Index_VI_Control_Misc0, 0x00, 0x02);
     watchdog = WATCHDOG_DELAY;
     while((!vblank_active_CRT2(pSiS, pPriv)) && --watchdog);
     watchdog = WATCHDOG_DELAY;
     while(vblank_active_CRT2(pSiS, pPriv) && --watchdog);

#ifdef SIS_CP
     SIS_CP_RESET_CP
#endif

  }

  if(pPriv->displayMode & (DISPMODE_SINGLE1 | DISPMODE_MIRROR)) {

     /* CRT1: Always uses overlay 0
      */

#ifdef SISDUALHEAD
     if(pPriv->dualHeadMode) {
	if(!pPriv->hasTwoOverlays) {
	   /* Check if overlay already grabbed by other head */
	   if(getsrreg(pSiS, 0x06) & 0x40) return;
	}
     }
#endif

     setvideoregmask(pSiS, Index_VI_Control_Misc2, 0x00, 0x05);
     setvideoregmask(pSiS, Index_VI_Control_Misc1, 0x00, 0x01);

     watchdog = WATCHDOG_DELAY;
     while((!vblank_active_CRT1(pSiS, pPriv)) && --watchdog);
     watchdog = WATCHDOG_DELAY;
     while(vblank_active_CRT1(pSiS, pPriv) && --watchdog);
     setvideoregmask(pSiS, Index_VI_Control_Misc0, 0x00, 0x02);
     watchdog = WATCHDOG_DELAY;
     while((!vblank_active_CRT1(pSiS, pPriv)) && --watchdog);
     watchdog = WATCHDOG_DELAY;
     while(vblank_active_CRT1(pSiS, pPriv) && --watchdog);

  }
}

/*********************************
 *         DisplayVideo()        *
 *********************************/

static void
SISDisplayVideo(ScrnInfoPtr pScrn, SISPortPrivPtr pPriv)
{
   SISPtr pSiS = SISPTR(pScrn);
#ifdef SISDUALHEAD
   SISEntPtr pSiSEnt = pSiS->entityPrivate;
#endif
   short  srcPitch = pPriv->srcPitch;
   short  height = pPriv->height;
   UShort screenwidth;
   SISOverlayRec overlay;
   int    srcOffsetX = 0, srcOffsetY = 0;
   int    sx = 0, sy = 0, watchdog;
   int    index = 0, iscrt2 = 0;
#ifdef SISMERGED
   UChar  temp;
   UShort screen2width = 0;
   int    srcOffsetX2 = 0, srcOffsetY2 = 0;
   int    sx2 = 0, sy2 = 0;
#endif

   /* Determine whether we have two overlays or only one */
   set_hastwooverlays(pSiS, pPriv);

   pPriv->NoOverlay = FALSE;
#ifdef SISDUALHEAD
   if(pPriv->dualHeadMode) {
      if(!pPriv->hasTwoOverlays) {
	 if(pSiS->SecondHead) {
	    if(pSiSEnt->curxvcrtnum != 0) {
	       if(pPriv->overlayStatus) {
		  close_overlay(pSiS, pPriv);
	       }
	       pPriv->NoOverlay = TRUE;
	       return;
	    }
	 } else {
	    if(pSiSEnt->curxvcrtnum != 1) {
	       if(pPriv->overlayStatus) {
		  close_overlay(pSiS, pPriv);
	       }
	       pPriv->NoOverlay = TRUE;
	       return;
	    }
	 }
      }
   }
#endif

   /* setup dispmode (MIRROR, SINGLEx) */
   set_dispmode(pScrn, pPriv);

   /* Check if overlay is supported with current mode */
#ifdef SISMERGED
   if(!pSiS->MergedFB) {
#endif
      if( ((pPriv->displayMode & DISPMODE_MIRROR) &&
           ((pSiS->MiscFlags & (MISC_CRT1OVERLAY|MISC_CRT2OVERLAY)) != (MISC_CRT1OVERLAY|MISC_CRT2OVERLAY))) ||
	  ((pPriv->displayMode & DISPMODE_SINGLE1) &&
	   (!(pSiS->MiscFlags & MISC_CRT1OVERLAY))) ||
	  ((pPriv->displayMode & DISPMODE_SINGLE2) &&
	   (!(pSiS->MiscFlags & MISC_CRT2OVERLAY))) ) {
	 if(pPriv->overlayStatus) {
	    close_overlay(pSiS, pPriv);
	 }
	 pPriv->NoOverlay = TRUE;
	 return;
      }
#ifdef SISMERGED
   }
#endif

   memset(&overlay, 0, sizeof(overlay));

   overlay.pixelFormat = pPriv->id;
   overlay.pitch = overlay.origPitch = srcPitch;
   if(pPriv->usechromakey) {
      overlay.keyOP = (pPriv->insidechromakey) ? VI_ROP_ChromaKey : VI_ROP_NotChromaKey;
   } else {
      overlay.keyOP = VI_ROP_DestKey;
   }

#ifdef SISDEINT
   switch(pPriv->deinterlacemethod) {
   case 1:
      overlay.bobEnable = 0x02;
      /* overlay.bobEnable |= (pPriv->currentBuf) ? 0x00 : 0x10; */
      break;
   case 2:
      overlay.bobEnable = 0x08;
      /* overlay.bobEnable |= (pPriv->currentBuf) ? 0x00 : 0x10; */
      break;
   case 3:
      overlay.bobEnable = 0x0a;
      /* overlay.bobEnable |= (pPriv->currentBuf) ? 0x00 : 0x10; */
      break;
   default:
#endif
      overlay.bobEnable = 0x00;    /* Disable BOB de-interlacer */
#ifdef SISDEINT
   }
#endif

#ifdef SISMERGED
   if(pSiS->MergedFB) {
      overlay.DoFirst = TRUE;
      overlay.DoSecond = TRUE;
      overlay.pitch2 = overlay.origPitch;
      overlay.currentmode = ((SiSMergedDisplayModePtr)pSiS->CurrentLayout.mode->Private)->CRT1;
      overlay.currentmode2 = ((SiSMergedDisplayModePtr)pSiS->CurrentLayout.mode->Private)->CRT2;
      overlay.SCREENheight  = overlay.currentmode->VDisplay;
      overlay.SCREENheight2 = overlay.currentmode2->VDisplay;
      screenwidth = overlay.currentmode->HDisplay;
      screen2width = overlay.currentmode2->HDisplay;
      overlay.dstBox.x1  = pPriv->drw_x - pSiS->CRT1frameX0;
      overlay.dstBox.x2  = overlay.dstBox.x1 + pPriv->drw_w;
      overlay.dstBox.y1  = pPriv->drw_y - pSiS->CRT1frameY0;
      overlay.dstBox.y2  = overlay.dstBox.y1 + pPriv->drw_h;
      overlay.dstBox2.x1 = pPriv->drw_x - pSiS->CRT2pScrn->frameX0;
      overlay.dstBox2.x2 = overlay.dstBox2.x1 + pPriv->drw_w;
      overlay.dstBox2.y1 = pPriv->drw_y - pSiS->CRT2pScrn->frameY0;
      overlay.dstBox2.y2 = overlay.dstBox2.y1 + pPriv->drw_h;
   } else {
#endif
      overlay.currentmode = pSiS->CurrentLayout.mode;
      overlay.SCREENheight = overlay.currentmode->VDisplay;
      screenwidth = overlay.currentmode->HDisplay;
      overlay.dstBox.x1 = pPriv->drw_x - pScrn->frameX0;
      overlay.dstBox.x2 = pPriv->drw_x + pPriv->drw_w - pScrn->frameX0;
      overlay.dstBox.y1 = pPriv->drw_y - pScrn->frameY0;
      overlay.dstBox.y2 = pPriv->drw_y + pPriv->drw_h - pScrn->frameY0;
#ifdef SISMERGED
   }
#endif

   /* Note: x2/y2 is actually real coordinate + 1 */

   if((overlay.dstBox.x1 >= overlay.dstBox.x2) ||
      (overlay.dstBox.y1 >= overlay.dstBox.y2)) {
#ifdef SISMERGED
      if(pSiS->MergedFB) overlay.DoFirst = FALSE;
      else
#endif
           return;
   }

   if((overlay.dstBox.x2 <= 0) || (overlay.dstBox.y2 <= 0)) {
#ifdef SISMERGED
      if(pSiS->MergedFB) overlay.DoFirst = FALSE;
      else
#endif
           return;
   }

   if((overlay.dstBox.x1 >= screenwidth) || (overlay.dstBox.y1 >= overlay.SCREENheight)) {
#ifdef SISMERGED
      if(pSiS->MergedFB) overlay.DoFirst = FALSE;
      else
#endif
           return;
   }

#ifdef SISMERGED
   if(pSiS->MergedFB) {
      /* Check if dotclock is within limits for CRT1 */
      if(pPriv->displayMode & (DISPMODE_SINGLE1 | DISPMODE_MIRROR)) {
         if(!(pSiS->MiscFlags & MISC_CRT1OVERLAY)) {
            overlay.DoFirst = FALSE;
         }
      }
   }
#endif

   if(overlay.dstBox.x1 < 0) {
      srcOffsetX = pPriv->src_w * (-overlay.dstBox.x1) / pPriv->drw_w;
      overlay.dstBox.x1 = 0;
   }
   if(overlay.dstBox.y1 < 0) {
      srcOffsetY = pPriv->src_h * (-overlay.dstBox.y1) / pPriv->drw_h;
      overlay.dstBox.y1 = 0;
   }

   if((overlay.dstBox.x1 >= overlay.dstBox.x2 - 2) ||
      (overlay.dstBox.x1 >= screenwidth - 2)       ||
      (overlay.dstBox.y1 >= overlay.dstBox.y2)) {
#ifdef SISMERGED
      if(pSiS->MergedFB) overlay.DoFirst = FALSE;
      else
#endif
           return;
   }

#ifdef SISMERGED
   if(pSiS->MergedFB) {
      if((overlay.dstBox2.x2 <= 0) || (overlay.dstBox2.y2 <= 0))
	 overlay.DoSecond = FALSE;

      if((overlay.dstBox2.x1 >= screen2width) || (overlay.dstBox2.y1 >= overlay.SCREENheight2))
	 overlay.DoSecond = FALSE;

      if(overlay.dstBox2.x1 < 0) {
	 srcOffsetX2 = pPriv->src_w * (-overlay.dstBox2.x1) / pPriv->drw_w;
	 overlay.dstBox2.x1 = 0;
      }

      if(overlay.dstBox2.y1 < 0) {
	 srcOffsetY2 = pPriv->src_h * (-overlay.dstBox2.y1) / pPriv->drw_h;
	 overlay.dstBox2.y1 = 0;
      }

      if((overlay.dstBox2.x1 >= overlay.dstBox2.x2 - 2) ||
	 (overlay.dstBox2.x1 >= screen2width - 2)       ||
	 (overlay.dstBox2.y1 >= overlay.dstBox2.y2))
	 overlay.DoSecond = FALSE;

      /* Check if dotclock is within limits for CRT2 */
      if(pPriv->displayMode & (DISPMODE_SINGLE2 | DISPMODE_MIRROR)) {
	 if(!(pSiS->MiscFlags & MISC_CRT2OVERLAY)) {
	    overlay.DoSecond = FALSE;
	 }
      }

      /* If neither overlay is to be displayed, disable them if they are currently enabled */
      if((!overlay.DoFirst) && (!overlay.DoSecond)) {
	 setvideoregmask(pSiS, Index_VI_Control_Misc2, 0x00, 0x05);
	 setvideoregmask(pSiS, Index_VI_Control_Misc1, 0x00, 0x01);
	 temp = getvideoreg(pSiS,Index_VI_Control_Misc0);
	 if(temp & 0x02) {
	    watchdog = WATCHDOG_DELAY;
	    if(pPriv->hasTwoOverlays) {
	       while((!vblank_active_CRT1(pSiS, pPriv)) && --watchdog);
	       watchdog = WATCHDOG_DELAY;
	       while(vblank_active_CRT1(pSiS, pPriv) && --watchdog);
	    } else {
	       temp = getsrreg(pSiS, 0x06);
	       if(!(temp & 0x40)) {
		  while((!vblank_active_CRT1(pSiS, pPriv)) && --watchdog);
		  watchdog = WATCHDOG_DELAY;
		  while(vblank_active_CRT1(pSiS, pPriv) && --watchdog);
	       } else {
		  while((!vblank_active_CRT2(pSiS, pPriv)) && --watchdog);
		  watchdog = WATCHDOG_DELAY;
		  while(vblank_active_CRT2(pSiS, pPriv) && --watchdog);
	       }
	    }
	    setvideoregmask(pSiS, Index_VI_Control_Misc0, 0x00, 0x02);
	 }
	 if(pPriv->hasTwoOverlays) {
	    setvideoregmask(pSiS, Index_VI_Control_Misc2, 0x01, 0x01);
	    setvideoregmask(pSiS, Index_VI_Control_Misc1, 0x00, 0x01);
	    temp = getvideoreg(pSiS,Index_VI_Control_Misc0);
	    if(temp & 0x02) {
	       watchdog = WATCHDOG_DELAY;
	       while((!vblank_active_CRT2(pSiS, pPriv)) && --watchdog);
	       watchdog = WATCHDOG_DELAY;
	       while(vblank_active_CRT2(pSiS, pPriv) && --watchdog);
	       setvideoregmask(pSiS, Index_VI_Control_Misc0, 0x00, 0x02);
	    }
	 }
	 pPriv->overlayStatus = FALSE;
         return;
      }
   }
#endif

   switch(pPriv->id) {

     case PIXEL_FMT_YV12:
       overlay.planar = 1;
       overlay.planar_shiftpitch = 1;
#ifdef SISMERGED
       if((!pSiS->MergedFB) || (overlay.DoFirst)) {
#endif
          sx = (pPriv->src_x + srcOffsetX) & ~7;
          sy = (pPriv->src_y + srcOffsetY) & ~1;
          overlay.PSY = pPriv->bufAddr[pPriv->currentBuf] + sx + sy*srcPitch;
          overlay.PSV = pPriv->bufAddr[pPriv->currentBuf] + height*srcPitch + ((sx + sy*srcPitch/2) >> 1);
          overlay.PSU = pPriv->bufAddr[pPriv->currentBuf] + height*srcPitch*5/4 + ((sx + sy*srcPitch/2) >> 1);
          overlay.PSY += FBOFFSET;
          overlay.PSV += FBOFFSET;
          overlay.PSU += FBOFFSET;
          overlay.PSY >>= pPriv->shiftValue;
          overlay.PSV >>= pPriv->shiftValue;
          overlay.PSU >>= pPriv->shiftValue;
#ifdef SISMERGED
       }
       if((pSiS->MergedFB) && (overlay.DoSecond)) {
          sx2 = (pPriv->src_x + srcOffsetX2) & ~7;
          sy2 = (pPriv->src_y + srcOffsetY2) & ~1;
          overlay.PSY2 = pPriv->bufAddr[pPriv->currentBuf] + sx2 + sy2*srcPitch;
          overlay.PSV2 = pPriv->bufAddr[pPriv->currentBuf] + height*srcPitch + ((sx2 + sy2*srcPitch/2) >> 1);
          overlay.PSU2 = pPriv->bufAddr[pPriv->currentBuf] + height*srcPitch*5/4 + ((sx2 + sy2*srcPitch/2) >> 1);
          overlay.PSY2 += FBOFFSET;
          overlay.PSV2 += FBOFFSET;
          overlay.PSU2 += FBOFFSET;
          overlay.PSY2 >>= pPriv->shiftValue;
          overlay.PSV2 >>= pPriv->shiftValue;
          overlay.PSU2 >>= pPriv->shiftValue;
       }
#endif
       break;

     case PIXEL_FMT_I420:
       overlay.planar = 1;
       overlay.planar_shiftpitch = 1;
#ifdef SISMERGED
       if((!pSiS->MergedFB) || (overlay.DoFirst)) {
#endif
          sx = (pPriv->src_x + srcOffsetX) & ~7;
          sy = (pPriv->src_y + srcOffsetY) & ~1;
          overlay.PSY = pPriv->bufAddr[pPriv->currentBuf] + sx + sy*srcPitch;
          overlay.PSV = pPriv->bufAddr[pPriv->currentBuf] + height*srcPitch*5/4 + ((sx + sy*srcPitch/2) >> 1);
          overlay.PSU = pPriv->bufAddr[pPriv->currentBuf] + height*srcPitch + ((sx + sy*srcPitch/2) >> 1);
          overlay.PSY += FBOFFSET;
          overlay.PSV += FBOFFSET;
          overlay.PSU += FBOFFSET;
          overlay.PSY >>= pPriv->shiftValue;
          overlay.PSV >>= pPriv->shiftValue;
          overlay.PSU >>= pPriv->shiftValue;
#ifdef SISMERGED
       }
       if((pSiS->MergedFB) && (overlay.DoSecond)) {
          sx2 = (pPriv->src_x + srcOffsetX2) & ~7;
          sy2 = (pPriv->src_y + srcOffsetY2) & ~1;
          overlay.PSY2 = pPriv->bufAddr[pPriv->currentBuf] + sx2 + sy2*srcPitch;
          overlay.PSV2 = pPriv->bufAddr[pPriv->currentBuf] + height*srcPitch*5/4 + ((sx2 + sy2*srcPitch/2) >> 1);
          overlay.PSU2 = pPriv->bufAddr[pPriv->currentBuf] + height*srcPitch + ((sx2 + sy2*srcPitch/2) >> 1);
          overlay.PSY2 += FBOFFSET;
          overlay.PSV2 += FBOFFSET;
          overlay.PSU2 += FBOFFSET;
          overlay.PSY2 >>= pPriv->shiftValue;
          overlay.PSV2 >>= pPriv->shiftValue;
          overlay.PSU2 >>= pPriv->shiftValue;
       }
#endif
       break;

     case PIXEL_FMT_NV12:
     case PIXEL_FMT_NV21:
       overlay.planar = 1;
       overlay.planar_shiftpitch = 0;
#ifdef SISMERGED
       if((!pSiS->MergedFB) || (overlay.DoFirst)) {
#endif
          sx = (pPriv->src_x + srcOffsetX) & ~7;
          sy = (pPriv->src_y + srcOffsetY) & ~1;
          overlay.PSY = pPriv->bufAddr[pPriv->currentBuf] + sx + sy*srcPitch;
          overlay.PSV =	pPriv->bufAddr[pPriv->currentBuf] + height*srcPitch + ((sx + sy*srcPitch/2) >> 1);
          overlay.PSY += FBOFFSET;
          overlay.PSV += FBOFFSET;
          overlay.PSY >>= pPriv->shiftValue;
          overlay.PSV >>= pPriv->shiftValue;
          overlay.PSU = overlay.PSV;
#ifdef SISMERGED
       }
       if((pSiS->MergedFB) && (overlay.DoSecond)) {
          sx2 = (pPriv->src_x + srcOffsetX2) & ~7;
          sy2 = (pPriv->src_y + srcOffsetY2) & ~1;
          overlay.PSY2 = pPriv->bufAddr[pPriv->currentBuf] + sx2 + sy2*srcPitch;
          overlay.PSV2 = pPriv->bufAddr[pPriv->currentBuf] + height*srcPitch + ((sx2 + sy2*srcPitch/2) >> 1);
          overlay.PSY2 += FBOFFSET;
          overlay.PSV2 += FBOFFSET;
          overlay.PSY2 >>= pPriv->shiftValue;
          overlay.PSV2 >>= pPriv->shiftValue;
          overlay.PSU2 = overlay.PSV2;
       }
#endif
       break;

     case PIXEL_FMT_YUY2:
     case PIXEL_FMT_UYVY:
     case PIXEL_FMT_YVYU:
     case PIXEL_FMT_RGB6:
     case PIXEL_FMT_RGB5:
     default:
       overlay.planar = 0;
#ifdef SISMERGED
       if((!pSiS->MergedFB) || (overlay.DoFirst)) {
#endif
          sx = (pPriv->src_x + srcOffsetX) & ~1;
          sy = (pPriv->src_y + srcOffsetY);
          overlay.PSY = (pPriv->bufAddr[pPriv->currentBuf] + sx*2 + sy*srcPitch);
          overlay.PSY += FBOFFSET;
          overlay.PSY >>= pPriv->shiftValue;
#ifdef SISMERGED
       }
       if((pSiS->MergedFB) && (overlay.DoSecond)) {
          sx2 = (pPriv->src_x + srcOffsetX2) & ~1;
          sy2 = (pPriv->src_y + srcOffsetY2);
          overlay.PSY2 = (pPriv->bufAddr[pPriv->currentBuf] + sx2*2 + sy2*srcPitch);
          overlay.PSY2 += FBOFFSET;
          overlay.PSY2 >>= pPriv->shiftValue;
       }
#endif
       break;
   }

   /* Some clipping checks */
#ifdef SISMERGED
   if((!pSiS->MergedFB) || (overlay.DoFirst)) {
#endif
      overlay.srcW = pPriv->src_w - (sx - pPriv->src_x);
      overlay.srcH = pPriv->src_h - (sy - pPriv->src_y);
      if( (pPriv->oldx1 != overlay.dstBox.x1) ||
	  (pPriv->oldx2 != overlay.dstBox.x2) ||
	  (pPriv->oldy1 != overlay.dstBox.y1) ||
	  (pPriv->oldy2 != overlay.dstBox.y2) ) {
	 pPriv->mustwait = 1;
	 pPriv->oldx1 = overlay.dstBox.x1; pPriv->oldx2 = overlay.dstBox.x2;
	 pPriv->oldy1 = overlay.dstBox.y1; pPriv->oldy2 = overlay.dstBox.y2;
      }
#ifdef SISMERGED
   }
   if((pSiS->MergedFB) && (overlay.DoSecond)) {
      overlay.srcW2 = pPriv->src_w - (sx2 - pPriv->src_x);
      overlay.srcH2 = pPriv->src_h - (sy2 - pPriv->src_y);
      if( (pPriv->oldx1_2 != overlay.dstBox2.x1) ||
	  (pPriv->oldx2_2 != overlay.dstBox2.x2) ||
	  (pPriv->oldy1_2 != overlay.dstBox2.y1) ||
	  (pPriv->oldy2_2 != overlay.dstBox2.y2) ) {
	 pPriv->mustwait = 1;
	 pPriv->oldx1_2 = overlay.dstBox2.x1; pPriv->oldx2_2 = overlay.dstBox2.x2;
	 pPriv->oldy1_2 = overlay.dstBox2.y1; pPriv->oldy2_2 = overlay.dstBox2.y2;
      }
   }
#endif

#ifdef SISMERGED
   /* Disable an overlay if it is not to be displayed (but enabled currently) */
   if((pSiS->MergedFB) && (pPriv->hasTwoOverlays)) {
      if(!overlay.DoFirst) {
	 setvideoregmask(pSiS, Index_VI_Control_Misc2, 0x00, 0x05);
	 setvideoregmask(pSiS, Index_VI_Control_Misc1, 0x00, 0x01);
	 temp = getvideoreg(pSiS,Index_VI_Control_Misc0);
	 if(temp & 0x02) {
	    watchdog = WATCHDOG_DELAY;
	    while((!vblank_active_CRT1(pSiS, pPriv)) && --watchdog);
	    watchdog = WATCHDOG_DELAY;
	    while(vblank_active_CRT1(pSiS, pPriv) && --watchdog);
	    setvideoregmask(pSiS, Index_VI_Control_Misc0, 0x00, 0x02);
	 }
      } else if(!overlay.DoSecond) {
	 setvideoregmask(pSiS, Index_VI_Control_Misc2, 0x01, 0x01);
	 setvideoregmask(pSiS, Index_VI_Control_Misc1, 0x00, 0x01);
	 temp = getvideoreg(pSiS,Index_VI_Control_Misc0);
	 if(temp & 0x02) {
	    watchdog = WATCHDOG_DELAY;
	    while((!vblank_active_CRT2(pSiS, pPriv)) && --watchdog);
	    watchdog = WATCHDOG_DELAY;
	    while(vblank_active_CRT2(pSiS, pPriv) && --watchdog);
	    setvideoregmask(pSiS, Index_VI_Control_Misc0, 0x00, 0x02);
	 }
      }
   }
#endif

   /* Loop head */
   /* Note: index can only be 1 for CRT2, ie overlay 1
    * is only used for CRT2.
    */
   if(pPriv->displayMode & DISPMODE_SINGLE2) {
      if(pPriv->hasTwoOverlays) {			/* We have 2 overlays: */
	 if(pPriv->dualHeadMode) {
	    /* Dual head: We use overlay 2 for CRT2 */
	    index = 1; iscrt2 = 1;
	 } else {
	    /* Single head: We use overlay 1 for CRT2 */
	    index = 0; iscrt2 = 1;
	 }
      } else {						/* We have 1 overlay */
	 /* We use that only overlay for CRT2 */
	 index = 0; iscrt2 = 1;
      }
      overlay.VBlankActiveFunc = vblank_active_CRT2;
#ifdef SISMERGED
      if(!pPriv->hasTwoOverlays) {
	 if((pSiS->MergedFB) && (!overlay.DoSecond)) {
	    index = 0; iscrt2 = 0;
	    overlay.VBlankActiveFunc = vblank_active_CRT1;
	    pPriv->displayMode = DISPMODE_SINGLE1;
	 }
      }
#endif
   } else {
      index = 0; iscrt2 = 0;
      overlay.VBlankActiveFunc = vblank_active_CRT1;
#ifdef SISMERGED
      if((pSiS->MergedFB) && (!overlay.DoFirst)) {
	 if(pPriv->hasTwoOverlays) index = 1;
	 iscrt2 = 1;
	 overlay.VBlankActiveFunc = vblank_active_CRT2;
	 if(!pPriv->hasTwoOverlays) {
	    pPriv->displayMode = DISPMODE_SINGLE2;
	 }
      }
#endif
   }

   /* set display mode SR06,32 (CRT1, CRT2 or mirror) */
   set_disptype_regs(pScrn, pPriv);

   /* set (not only calc) merge line buffer */
#ifdef SISMERGED
   if(!pSiS->MergedFB) {
#endif
      merge_line_buf(pSiS, pPriv, (overlay.srcW > pPriv->linebufMergeLimit), overlay.srcW,
      		     pPriv->linebufMergeLimit);
#ifdef SISMERGED
   } else {
      Bool temp1 = FALSE, temp2 = FALSE;
      if(overlay.DoFirst) {
         if(overlay.srcW > pPriv->linebufMergeLimit)  temp1 = TRUE;
      }
      if(overlay.DoSecond) {
         if(overlay.srcW2 > pPriv->linebufMergeLimit) temp2 = TRUE;
      }
      merge_line_buf_mfb(pSiS, pPriv, temp1, temp2, overlay.srcW, overlay.srcW2,
			 pPriv->linebufMergeLimit);
   }
#endif

   /* calculate (not set!) line buffer length */
#ifdef SISMERGED
   if((!pSiS->MergedFB) || (overlay.DoFirst))
#endif
      calc_line_buf_size_1(&overlay, pPriv);
#ifdef SISMERGED
   if((pSiS->MergedFB) && (overlay.DoSecond))
      calc_line_buf_size_2(&overlay, pPriv);
#endif

   if(pPriv->dualHeadMode) {
#ifdef SISDUALHEAD
      if(!pSiS->SecondHead) {
         if(pPriv->updatetvxpos) {
            SiS_SetTVxposoffset(pScrn, pPriv->tvxpos);
            pPriv->updatetvxpos = FALSE;
         }
         if(pPriv->updatetvypos) {
            SiS_SetTVyposoffset(pScrn, pPriv->tvypos);
            pPriv->updatetvypos = FALSE;
         }
      }
#endif
   } else {
      if(pPriv->updatetvxpos) {
         SiS_SetTVxposoffset(pScrn, pPriv->tvxpos);
         pPriv->updatetvxpos = FALSE;
      }
      if(pPriv->updatetvypos) {
         SiS_SetTVyposoffset(pScrn, pPriv->tvypos);
         pPriv->updatetvypos = FALSE;
      }
   }

#if 0 /* Clearing this does not seem to be required */
      /* and might even be dangerous. */
   if(pSiS->VGAEngine == SIS_315_VGA) {
      watchdog = WATCHDOG_DELAY;
      while(overlay.VBlankActiveFunc(pSiS, pPriv) && --watchdog);
      setvideoregmask(pSiS, Index_VI_Control_Misc3, 0x00, 0x03);
   }
#endif
   setvideoregmask(pSiS, Index_VI_Control_Misc3, 0x03, 0x03);

   /* Do the following in a loop for CRT1 and CRT2 ----------------- */
MIRROR:

   /* calculate scale factor */
#ifdef SISMERGED
   if(pSiS->MergedFB && iscrt2)
      calc_scale_factor_2(&overlay, pScrn, pPriv, index, iscrt2);
   else
#endif
      calc_scale_factor(&overlay, pScrn, pPriv, index, iscrt2);

   /* Select overlay 0 (used for CRT1/or CRT2) or overlay 1 (used for CRT2 only) */
   setvideoregmask(pSiS, Index_VI_Control_Misc2, index, 0x01);

   /* set format (before color and chroma keys) */
   set_format(pSiS, &overlay);

   /* set color key */
   set_colorkey(pSiS, pPriv->colorKey);

   if(pPriv->usechromakey) {
      /* Select chroma key format (300 series only) */
      if(pSiS->VGAEngine == SIS_300_VGA) {
	 setvideoregmask(pSiS, Index_VI_Control_Misc0,
	                 (pPriv->yuvchromakey ? 0x40 : 0x00), 0x40);
      }
      set_chromakey(pSiS, pPriv->chromamin, pPriv->chromamax);
   }

   /* set brightness, contrast, hue, saturation */
   set_brightness(pSiS, pPriv->brightness);
   set_contrast(pSiS, pPriv->contrast);
   if(pSiS->VGAEngine == SIS_315_VGA) {
      set_hue(pSiS, pPriv->hue);
      set_saturation(pSiS, pPriv->saturation);
   }

   /* enable/disable graphics display around overlay
    * (Since disabled overlays don't get treated in this
    * loop, we omit respective checks here)
    */
   if(!iscrt2) {
     set_disablegfx(pSiS, pPriv->disablegfx, &overlay);
   } else if(!pSiS->hasTwoOverlays) {
     set_disablegfx(pSiS, FALSE, &overlay);
   }
   set_disablegfxlr(pSiS, pPriv->disablegfxlr, &overlay);

#ifdef SIS_CP
   SIS_CP_VIDEO_SET_CP
#endif

   /* set remaining overlay parameters */
   set_overlay(pSiS, &overlay, pPriv, index, iscrt2);

   /* enable overlay */
   setvideoregmask (pSiS, Index_VI_Control_Misc0, 0x02, 0x02);

   /* loop foot */
   if(pPriv->displayMode & DISPMODE_MIRROR &&
      index == 0			   &&
      pPriv->hasTwoOverlays) {
#ifdef SISMERGED
      if((!pSiS->MergedFB) || overlay.DoSecond) {
#endif
	 index = 1; iscrt2 = 1;
	 overlay.VBlankActiveFunc = vblank_active_CRT2;
	 goto MIRROR;
#ifdef SISMERGED
      }
#endif
   }

   /* Now for the trigger: This is a bad hack to work-around
    * an obvious hardware bug: Overlay 1 (which is ONLY used
    * for CRT2 in this driver) does not always update its
    * window position and some other stuff. Earlier, this was
    * solved be disabling the overlay, but this took forever
    * and was ugly on the screen.
    * Now: We write 0x03 to 0x74 from the beginning. This is
    * meant as a "lock" - the driver is supposed to write 0
    * to this register, bit 0 for overlay 0, bit 1 for over-
    * lay 1, then change buffer addresses, pitches, window
    * position, scaler registers, format, etc., then write
    * 1 to 0x74. The hardware then reads the registers into
    * its internal engine and clears these bits.
    * All this works for overlay 0, but not 1. Overlay 1
    * has assumingly the following restrictions:
    * - New data written to the registers is only read
    *   correctly by the engine if the registers are written
    *   when the current scanline is beyond the current
    *   overlay position and below the maximum visible
    *   scanline (vertical screen resolution)
    * - If a vertical retrace occures during writing the
    *   registers, the registers written BEFORE this re-
    *   trace happened, are not being read into the
    *   engine if the trigger is set after the retrace.
    * Therefore: We write the overlay registers above in
    * set_overlay only if the scanline matches, and save
    * the then current scanline. If this scanline is higher
    * than the now current scanline, we assume a retrace,
    * wait for the scanline to match the criteria above again,
    * and rewrite all relevant registers.
    * I have no idea if this is meant that way, but after
    * fiddling three entire days with this crap, I found this
    * to be the only solution.
    */
   if(pSiS->VGAEngine == SIS_315_VGA) {
      if((pPriv->mustwait) && index) {
	 watchdog = get_scanline_CRT2(pSiS, pPriv);
	 if(watchdog <= overlay.oldLine) {
	    int i, mytop = overlay.oldtop;
	    int screenHeight = overlay.SCREENheight;
#ifdef SISMERGED
	    if(pSiS->MergedFB) {
	       screenHeight = overlay.SCREENheight2;
	    }
#endif
	    if(mytop < screenHeight - 2) {
	       do {
	          watchdog = get_scanline_CRT2(pSiS, pPriv);
	       } while((watchdog <= mytop) || (watchdog >= screenHeight));
	    }
	    for(i=0x02; i<=0x12; i++) {
	       setvideoreg(pSiS, i, getvideoreg(pSiS, i));
	    }
	    for(i=0x18; i<=0x1c; i++) {
	       setvideoreg(pSiS, i, getvideoreg(pSiS, i));
	    }
	    for(i=0x2c; i<=0x2e; i++) {
	       setvideoreg(pSiS, i, getvideoreg(pSiS, i));
	    }
	    for(i=0x6b; i<=0x6f; i++) {
	       setvideoreg(pSiS, i, getvideoreg(pSiS, i));
	    }
	 }
      }
      /* Trigger register copy for 315/330 series */
      setvideoregmask(pSiS, Index_VI_Control_Misc3, 0x03, 0x03);
   }

   pPriv->mustwait = 0;
   pPriv->overlayStatus = TRUE;
}

/*********************************
 *        Memory management      *
 *********************************/

#ifdef SIS_USE_EXA
static void
SiSDestroyArea(ScreenPtr pScreen, ExaOffscreenArea *area)
{
   void **handle = (void *)area->privData;
   *handle = NULL;
}
#endif

unsigned int
SISAllocateFBMemory(
  ScrnInfoPtr pScrn,
  void **handle,
  int bytesize
){
   SISPtr pSiS = SISPTR(pScrn);
   ScreenPtr pScreen = screenInfo.screens[pScrn->scrnIndex];

#ifdef SIS_USE_XAA
   if(!pSiS->useEXA) {
      FBLinearPtr linear = (FBLinearPtr)(*handle);
      FBLinearPtr new_linear;
      int depth = pSiS->CurrentLayout.bitsPerPixel >> 3;
      int size = ((bytesize + depth - 1) / depth);

      if(linear) {
         if(linear->size >= size) {
	    return (unsigned int)(linear->offset * depth);
	 }

         if(xf86ResizeOffscreenLinear(linear, size)) {
	    return (unsigned int)(linear->offset * depth);
	 }

         xf86FreeOffscreenLinear(linear);
	 *handle = NULL;
      }

      new_linear = xf86AllocateOffscreenLinear(pScreen, size, 8, NULL, NULL, NULL);

      if(!new_linear) {
         int max_size;

         xf86QueryLargestOffscreenLinear(pScreen, &max_size, 8, PRIORITY_EXTREME);

         if(max_size < size)
	    return 0;

         xf86PurgeUnlockedOffscreenAreas(pScreen);
         new_linear = xf86AllocateOffscreenLinear(pScreen, size, 8, NULL, NULL, NULL);
      }
      if(!new_linear) {
         xf86DrvMsg(pScrn->scrnIndex, X_INFO,
	           "Xv: Failed to allocate %d pixels of linear video memory\n", size);
	 return 0;
      } else {
         *handle = (void *)new_linear;
	 return (unsigned int)(new_linear->offset * depth);
      }
   }
#endif
#ifdef SIS_USE_EXA
   if(pSiS->useEXA && !pSiS->NoAccel) {
      ExaOffscreenArea *area = (ExaOffscreenArea *)(*handle);

      if(area) {
	 if(area->size >= bytesize) return (unsigned int)(area->offset);

	 exaOffscreenFree(pScreen, area);
	 *handle = NULL;
      }

      if(!(area = exaOffscreenAlloc(pScreen, bytesize, 8, TRUE, SiSDestroyArea, (pointer)handle))) {
	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
	           "Xv: Failed to allocate %d bytes of video memory\n", bytesize);
	 return 0;
      } else {
	 *handle = (void *)area;
	 return (unsigned int)(area->offset);
      }
   }
#endif

   return 0;
}

void
SISFreeFBMemory(ScrnInfoPtr pScrn, void **handle)
{
    SISPtr pSiS = SISPTR(pScrn);
#ifdef SIS_USE_EXA
    ScreenPtr pScreen = screenInfo.screens[pScrn->scrnIndex];
#endif

#ifdef SIS_USE_XAA
    if(!pSiS->useEXA) {
       if(*handle) {
          xf86FreeOffscreenLinear((FBLinearPtr)(*handle));
       }
    }
#endif
#ifdef SIS_USE_EXA
   if(pSiS->useEXA && !pSiS->NoAccel) {
      if(*handle) {
         exaOffscreenFree(pScreen, (ExaOffscreenArea *)(*handle));
      }
   }
#endif
   *handle = NULL;
}

/*********************************
 *           StopVideo()         *
 *********************************/

static void
SISStopVideo(ScrnInfoPtr pScrn, pointer data, Bool shutdown)
{
  SISPortPrivPtr pPriv = (SISPortPrivPtr)data;
  SISPtr pSiS = SISPTR(pScrn);

  if(pPriv->grabbedByV4L) return;

  REGION_EMPTY(pScrn->pScreen, &pPriv->clip);

  if(shutdown) {
     if(pPriv->videoStatus & CLIENT_VIDEO_ON) {
        close_overlay(pSiS, pPriv);
        pPriv->mustwait = 1;
     }
     SISFreeFBMemory(pScrn, &pPriv->handle);
     pPriv->videoStatus = 0;
  } else {
     if(pPriv->videoStatus & CLIENT_VIDEO_ON) {
        UpdateCurrentTime();
        pPriv->offTime = currentTime.milliseconds + OFF_DELAY;
        pPriv->videoStatus = OFF_TIMER | CLIENT_VIDEO_ON;
        pSiS->VideoTimerCallback = SISVideoTimerCallback;
     }
  }
}

/*********************************
 *          PutImage()           *
 *********************************/

static int
SISPutImage(
  ScrnInfoPtr pScrn,
  short src_x, short src_y,
  short drw_x, short drw_y,
  short src_w, short src_h,
  short drw_w, short drw_h,
  int id, UChar *buf,
  short width, short height,
  Bool sync,
  RegionPtr clipBoxes, pointer data,
  DrawablePtr pDraw
){
   SISPtr pSiS = SISPTR(pScrn);
   SISPortPrivPtr pPriv = (SISPortPrivPtr)data;
#ifdef SIS_USE_XAA
   XAAInfoRecPtr pXAA = pSiS->AccelInfoPtr;
   int depth = pSiS->CurrentLayout.bitsPerPixel >> 3;
   int myreds[] = { 0x000000ff, 0x0000f800, 0, 0x00ff0000 };
#endif
   int totalSize = 0;
#ifdef SISDEINT
   Bool	deintfm = (pPriv->deinterlacemethod > 1) ? TRUE : FALSE;
#endif

#if 0
   xf86DrvMsg(0, X_INFO, "PutImage: src %dx%d-%dx%d, drw %dx%d-%dx%d, id %x, w %d h %d, buf %p\n",
	src_x, src_y, src_w, src_h, drw_x, drw_y, drw_w, drw_h, id, width, height, buf);
#endif

   if(pPriv->grabbedByV4L) return Success;

   pPriv->drw_x = drw_x;
   pPriv->drw_y = drw_y;
   pPriv->drw_w = drw_w;
   pPriv->drw_h = drw_h;
   pPriv->src_x = src_x;
   pPriv->src_y = src_y;
   pPriv->src_w = src_w;
   pPriv->src_h = src_h;
   pPriv->id = id;
   pPriv->height = height;

   /* Pixel formats:
      1. YU12:  3 planes:       H    V
               Y sample period  1    1   (8 bit per pixel)
	       V sample period  2    2	 (8 bit per pixel, subsampled)
	       U sample period  2    2   (8 bit per pixel, subsampled)

	 Y plane is fully sampled (width*height), U and V planes
	 are sampled in 2x2 blocks, hence a group of 4 pixels requires
	 4 + 1 + 1 = 6 bytes. The data is planar, ie in single planes
	 for Y, U and V.
      2. UYVY: 3 planes:        H    V
	       Y sample period  1    1   (8 bit per pixel)
	       V sample period  2    1	 (8 bit per pixel, subsampled)
	       U sample period  2    1   (8 bit per pixel, subsampled)
	 Y plane is fully sampled (width*height), U and V planes
	 are sampled in 2x1 blocks, hence a group of 4 pixels requires
	 4 + 2 + 2 = 8 bytes. The data is bit packed, there are no separate
	 Y, U or V planes.
	 Bit order:  U0 Y0 V0 Y1  U2 Y2 V2 Y3 ...
      3. I420: Like YU12, but planes U and V are in reverse order.
      4. YUY2: Like UYVY, but order is
		     Y0 U0 Y1 V0  Y2 U2 Y3 V2 ...
      5. YVYU: Like YUY2, but order is
		     Y0 V0 Y1 U0  Y2 V2 Y3 U2 ...
      6. NV12, NV21: 2 planes   H    V
	       Y sample period  1    1   (8 bit per pixel)
	       V sample period  2    1	 (8 bit per pixel, subsampled)
	       U sample period  2    1   (8 bit per pixel, subsampled)
	 Y plane is fully samples (width*height), U and V planes are
	 interleaved in memory (one byte U, one byte V for NV12, NV21
	 other way round) and sampled in 2x1 blocks. Otherwise such
	 as all other planar formats.
   */

   switch(id){
     case PIXEL_FMT_YV12:
     case PIXEL_FMT_I420:
     case PIXEL_FMT_NV12:
     case PIXEL_FMT_NV21:
       pPriv->srcPitch = (width + 7) & ~7;
       /* Size = width * height * 3 / 2 */
       totalSize = (pPriv->srcPitch * height * 3) >> 1; /* Verified */
       break;
     case PIXEL_FMT_YUY2:
     case PIXEL_FMT_UYVY:
     case PIXEL_FMT_YVYU:
     case PIXEL_FMT_RGB6:
     case PIXEL_FMT_RGB5:
     default:
       pPriv->srcPitch = ((width << 1) + 3) & ~3;	/* Verified */
       /* Size = width * 2 * height */
       totalSize = pPriv->srcPitch * height;
   }

   /* make it a multiple of 16 to simplify to copy loop */
   totalSize += 15;
   totalSize &= ~15; /* in bytes */

   /* allocate memory (we do doublebuffering) - size is in bytes */
   if(!(pPriv->bufAddr[0] = SISAllocateFBMemory(pScrn, &pPriv->handle, totalSize << 1)))
      return BadAlloc;

#ifdef SISDEINT
   if(deintfm) {
      pPriv->bufAddr[1] = pPriv->bufAddr[0] + pPriv->srcPitch;

      {
         CARD8 *src = (CARD8 *)buf;
         CARD8 *dest = (CARD8 *)(pSiS->FbBase + pPriv->bufAddr[pPriv->currentBuf]);
         int i = height;
         while(i--) {
	    SiSMemCopyToVideoRam(pSiS, dest, src, pPriv->srcPitch);
	    src += pPriv->srcPitch;
	    dest += (pPriv->srcPitch << 1);
         }
      }

   } else {
#endif
      pPriv->bufAddr[1] = pPriv->bufAddr[0] + totalSize;

      /* copy data */
      if((pSiS->XvUseMemcpy) || (totalSize < 16)) {
         SiSMemCopyToVideoRam(pSiS, pSiS->FbBase + pPriv->bufAddr[pPriv->currentBuf], buf, totalSize);
      } else {
         ULong i;
         CARD32 *src = (CARD32 *)buf;
         CARD32 *dest = (CARD32 *)(pSiS->FbBase + pPriv->bufAddr[pPriv->currentBuf]);
         for(i = 0; i < (totalSize/16); i++) {
            *dest++ = *src++;
	    *dest++ = *src++;
	    *dest++ = *src++;
	    *dest++ = *src++;
         }
      }
#ifdef SISDEINT
   }
#endif

   SISDisplayVideo(pScrn, pPriv);

   /* update cliplist */
   if(pPriv->autopaintColorKey &&
      (pPriv->grabbedByV4L ||
#if XF86_VERSION_CURRENT < XF86_VERSION_NUMERIC(4,3,99,3,0)
       (!RegionsEqual(&pPriv->clip, clipBoxes)) ||
#else
       (!REGION_EQUAL(pScrn->pScreen, &pPriv->clip, clipBoxes)) ||
#endif
       (pPriv->PrevOverlay != pPriv->NoOverlay))) {
     /* We always paint the colorkey for V4L */
     if(!pPriv->grabbedByV4L) {
     	REGION_COPY(pScrn->pScreen, &pPriv->clip, clipBoxes);
     }
     /* draw these */
     pPriv->PrevOverlay = pPriv->NoOverlay;
#ifdef SIS_USE_XAA
     if((pPriv->NoOverlay) && pXAA && pXAA->FillMono8x8PatternRects) {
        (*pXAA->FillMono8x8PatternRects)(pScrn, myreds[depth-1],
			0x000000, GXcopy, ~0,
			REGION_NUM_RECTS(clipBoxes),
			REGION_RECTS(clipBoxes),
			0x00422418, 0x18244200, 0, 0);
     } else {
#endif
        if(!pSiS->disablecolorkeycurrent) {
#if XF86_VERSION_CURRENT < XF86_VERSION_NUMERIC(4,1,99,1,0)
           (*pXAA->FillSolidRects)(pScrn, pPriv->colorKey, GXcopy, ~0,
                           REGION_NUM_RECTS(clipBoxes),
                           REGION_RECTS(clipBoxes));
#else
	   xf86XVFillKeyHelper(pScrn->pScreen, (pPriv->NoOverlay) ? 0x00ff0000 : pPriv->colorKey, clipBoxes);
#endif
	}
#ifdef SIS_USE_XAA
     }
#endif

   }

   pPriv->currentBuf ^= 1;

   pPriv->videoStatus = CLIENT_VIDEO_ON;

   pSiS->VideoTimerCallback = SISVideoTimerCallback;

   return Success;
}

/*********************************
 *     QueryImageAttributes()    *
 *********************************/

static int
SISQueryImageAttributes(
  ScrnInfoPtr pScrn,
  int id,
  UShort *w, UShort *h,
  int *pitches, int *offsets
){
    int    pitchY, pitchUV;
    int    size, sizeY, sizeUV;

    if(*w < IMAGE_MIN_WIDTH) *w = IMAGE_MIN_WIDTH;
    if(*h < IMAGE_MIN_HEIGHT) *h = IMAGE_MIN_HEIGHT;

    if(*w > DummyEncoding.width) *w = DummyEncoding.width;
    if(*h > DummyEncoding.height) *h = DummyEncoding.height;

    switch(id) {
    case PIXEL_FMT_YV12:
    case PIXEL_FMT_I420:
        *w = (*w + 7) & ~7;
        *h = (*h + 1) & ~1;
        pitchY = *w;
    	pitchUV = *w >> 1;
    	if(pitches) {
      	    pitches[0] = pitchY;
            pitches[1] = pitches[2] = pitchUV;
        }
    	sizeY = pitchY * (*h);
    	sizeUV = pitchUV * ((*h) >> 1);
    	if(offsets) {
          offsets[0] = 0;
          offsets[1] = sizeY;
          offsets[2] = sizeY + sizeUV;
        }
        size = sizeY + (sizeUV << 1);
    	break;
    case PIXEL_FMT_NV12:
    case PIXEL_FMT_NV21:
        *w = (*w + 7) & ~7;
        *h = (*h + 1) & ~1;
	pitchY = *w;
    	pitchUV = *w;
    	if(pitches) {
      	    pitches[0] = pitchY;
            pitches[1] = pitchUV;
        }
    	sizeY = pitchY * (*h);
    	sizeUV = pitchUV * ((*h) >> 1);
    	if(offsets) {
          offsets[0] = 0;
          offsets[1] = sizeY;
        }
        size = sizeY + (sizeUV << 1);
        break;
    case PIXEL_FMT_YUY2:
    case PIXEL_FMT_UYVY:
    case PIXEL_FMT_YVYU:
    case PIXEL_FMT_RGB6:
    case PIXEL_FMT_RGB5:
    default:
        *w = (*w + 1) & ~1;
        pitchY = *w << 1;
    	if(pitches) pitches[0] = pitchY;
    	if(offsets) offsets[0] = 0;
    	size = pitchY * (*h);
    	break;
    }

    return size;
}


/*********************************
 *      OFFSCREEN SURFACES       *
 *********************************/

static int
SISAllocSurface (
    ScrnInfoPtr pScrn,
    int id,
    UShort w,
    UShort h,
    XF86SurfacePtr surface
)
{
    SISPtr pSiS = SISPTR(pScrn);
    SISPortPrivPtr pPriv = GET_PORT_PRIVATE(pScrn);
    int size;

    if((w < IMAGE_MIN_WIDTH) || (h < IMAGE_MIN_HEIGHT))
       return BadValue;

    if((w > DummyEncoding.width) || (h > DummyEncoding.height))
       return BadValue;

    if(pPriv->grabbedByV4L)
       return BadAlloc;

    w = (w + 1) & ~1;
    pPriv->pitch = ((w << 1) + 63) & ~63; /* Only packed pixel modes supported */
    size = h * pPriv->pitch;
    if(!(pPriv->offset = SISAllocateFBMemory(pScrn, &pPriv->handle, size)))
       return BadAlloc;

    surface->width   = w;
    surface->height  = h;
    surface->pScrn   = pScrn;
    surface->id      = id;
    surface->pitches = &pPriv->pitch;
    surface->offsets = &pPriv->offset;
    surface->devPrivate.ptr = (pointer)pPriv;

    close_overlay(pSiS, pPriv);
    pPriv->videoStatus = 0;
    REGION_EMPTY(pScrn->pScreen, &pPriv->clip);
    pSiS->VideoTimerCallback = NULL;
    pPriv->grabbedByV4L = TRUE;
    return Success;
}

static int
SISStopSurface (XF86SurfacePtr surface)
{
    SISPortPrivPtr pPriv = (SISPortPrivPtr)(surface->devPrivate.ptr);
    SISPtr pSiS = SISPTR(surface->pScrn);

    if(pPriv->grabbedByV4L && pPriv->videoStatus) {
       close_overlay(pSiS, pPriv);
       pPriv->mustwait = 1;
       pPriv->videoStatus = 0;
    }
    return Success;
}

static int
SISFreeSurface (XF86SurfacePtr surface)
{
    SISPortPrivPtr pPriv = (SISPortPrivPtr)(surface->devPrivate.ptr);

    if(pPriv->grabbedByV4L) {
       SISStopSurface(surface);
       SISFreeFBMemory(surface->pScrn, &pPriv->handle);
       pPriv->grabbedByV4L = FALSE;
    }
    return Success;
}

static int
SISGetSurfaceAttribute (
    ScrnInfoPtr pScrn,
    Atom attribute,
    INT32 *value
)
{
    SISPortPrivPtr pPriv = GET_PORT_PRIVATE(pScrn);

    return SISGetPortAttribute(pScrn, attribute, value, (pointer)pPriv);
}

static int
SISSetSurfaceAttribute(
    ScrnInfoPtr pScrn,
    Atom attribute,
    INT32 value
)
{
    SISPortPrivPtr pPriv = GET_PORT_PRIVATE(pScrn);;

    return SISSetPortAttribute(pScrn, attribute, value, (pointer)pPriv);
}

static int
SISDisplaySurface (
    XF86SurfacePtr surface,
    short src_x, short src_y,
    short drw_x, short drw_y,
    short src_w, short src_h,
    short drw_w, short drw_h,
    RegionPtr clipBoxes
)
{
   ScrnInfoPtr pScrn = surface->pScrn;
   SISPortPrivPtr pPriv = (SISPortPrivPtr)(surface->devPrivate.ptr);
#ifdef SIS_USE_XAA
   SISPtr pSiS = SISPTR(pScrn);
   int myreds[] = { 0x000000ff, 0x0000f800, 0, 0x00ff0000 };
#endif

   if(!pPriv->grabbedByV4L) return Success;

   pPriv->drw_x = drw_x;
   pPriv->drw_y = drw_y;
   pPriv->drw_w = drw_w;
   pPriv->drw_h = drw_h;
   pPriv->src_x = src_x;
   pPriv->src_y = src_y;
   pPriv->src_w = src_w;
   pPriv->src_h = src_h;
   pPriv->id = surface->id;
   pPriv->height = surface->height;
   pPriv->bufAddr[0] = surface->offsets[0];
   pPriv->currentBuf = 0;
   pPriv->srcPitch = surface->pitches[0];

   SISDisplayVideo(pScrn, pPriv);

   if(pPriv->autopaintColorKey) {
#ifdef SIS_USE_XAA
      XAAInfoRecPtr pXAA = pSiS->AccelInfoPtr;

      if((pPriv->NoOverlay) && pXAA && pXAA->FillMono8x8PatternRects) {
         (*pXAA->FillMono8x8PatternRects)(pScrn,
	  		myreds[(pSiS->CurrentLayout.bitsPerPixel >> 3) - 1],
	 		0x000000, GXcopy, ~0,
			REGION_NUM_RECTS(clipBoxes),
			REGION_RECTS(clipBoxes),
			0x00422418, 0x18244200, 0, 0);

      } else {
#endif
#if XF86_VERSION_CURRENT < XF86_VERSION_NUMERIC(4,1,99,1,0)
   	 (*pXAA->FillSolidRects)(pScrn, pPriv->colorKey, GXcopy, ~0,
                        REGION_NUM_RECTS(clipBoxes),
                        REGION_RECTS(clipBoxes));
#else
         xf86XVFillKeyHelper(pScrn->pScreen, (pPriv->NoOverlay) ? 0x00ff0000 : pPriv->colorKey, clipBoxes);
#endif
#ifdef SIS_USE_XAA
      }
#endif
   }

   pPriv->videoStatus = CLIENT_VIDEO_ON;

   return Success;
}

#define NUMOFFSCRIMAGES_300 4
#define NUMOFFSCRIMAGES_315 5

static XF86OffscreenImageRec SISOffscreenImages[NUMOFFSCRIMAGES_315] =
{
 {
   &SISImages[0],  	/* YUV2 */
   VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT,
   SISAllocSurface,
   SISFreeSurface,
   SISDisplaySurface,
   SISStopSurface,
   SISGetSurfaceAttribute,
   SISSetSurfaceAttribute,
   0, 0,  			/* Rest will be filled in */
   0,
   NULL
 },
 {
   &SISImages[2],	/* UYVY */
   VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT,
   SISAllocSurface,
   SISFreeSurface,
   SISDisplaySurface,
   SISStopSurface,
   SISGetSurfaceAttribute,
   SISSetSurfaceAttribute,
   0, 0,  			/* Rest will be filled in */
   0,
   NULL
 }
 ,
 {
   &SISImages[4],	/* RV15 */
   VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT,
   SISAllocSurface,
   SISFreeSurface,
   SISDisplaySurface,
   SISStopSurface,
   SISGetSurfaceAttribute,
   SISSetSurfaceAttribute,
   0, 0,  			/* Rest will be filled in */
   0,
   NULL
 },
 {
   &SISImages[5],	/* RV16 */
   VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT,
   SISAllocSurface,
   SISFreeSurface,
   SISDisplaySurface,
   SISStopSurface,
   SISGetSurfaceAttribute,
   SISSetSurfaceAttribute,
   0, 0,  			/* Rest will be filled in */
   0,
   NULL
 },
 {
   &SISImages[6],	/* YVYU */
   VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT,
   SISAllocSurface,
   SISFreeSurface,
   SISDisplaySurface,
   SISStopSurface,
   SISGetSurfaceAttribute,
   SISSetSurfaceAttribute,
   0, 0,  			/* Rest will be filled in */
   0,
   NULL
 }
};

static void
SISInitOffscreenImages(ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    SISPtr pSiS = SISPTR(pScrn);
    int i, num;

    if(pSiS->VGAEngine == SIS_300_VGA)	num = NUMOFFSCRIMAGES_300;
    else 				num = NUMOFFSCRIMAGES_315;

    for(i = 0; i < num; i++) {
       SISOffscreenImages[i].max_width  = DummyEncoding.width;
       SISOffscreenImages[i].max_height = DummyEncoding.height;
       if(pSiS->VGAEngine == SIS_300_VGA) {
          SISOffscreenImages[i].attributes = &SISAttributes_300[0];
	  SISOffscreenImages[i].num_attributes = SiSCountAttributes(&SISAttributes_300[0]);
       } else {
          SISOffscreenImages[i].attributes = &SISAttributes_315[0];
	  SISOffscreenImages[i].num_attributes = SiSCountAttributes(&SISAttributes_315[0]);
	  if((pSiS->hasTwoOverlays) && (!(pSiS->SiS_SD2_Flags & SiS_SD2_SUPPORT760OO))) {
	     SISOffscreenImages[i].num_attributes--;
	  }
       }
    }
    xf86XVRegisterOffscreenImages(pScreen, SISOffscreenImages, num);
}

/*****************************************************************/
/*                         BLIT ADAPTORS                         */
/*****************************************************************/
#ifdef INCL_YUV_BLIT_ADAPTOR

static void
SISSetPortDefaultsBlit(ScrnInfoPtr pScrn, SISBPortPrivPtr pPriv)
{
    /* Default: Don't sync. */
    pPriv->vsync  = 0;
}

static void
SISResetVideoBlit(ScrnInfoPtr pScrn)
{
}

static XF86VideoAdaptorPtr
SISSetupBlitVideo(ScreenPtr pScreen)
{
   ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
   SISPtr pSiS = SISPTR(pScrn);
   XF86VideoAdaptorPtr adapt;
   SISBPortPrivPtr pPriv;
   int i;

#ifdef SIS_USE_XAA
   if(!pSiS->useEXA) {
      if(!pSiS->AccelInfoPtr) return NULL;
   }
#endif

   if(!(adapt = calloc(1, sizeof(XF86VideoAdaptorRec) +
    			   (sizeof(DevUnion) * NUM_BLIT_PORTS) +
                           sizeof(SISBPortPrivRec)))) {
      return NULL;
   }

   adapt->type = XvWindowMask | XvInputMask | XvImageMask;
   adapt->flags = 0;
   adapt->name = "SIS 315/330 series Video Blitter";
   adapt->nEncodings = 1;
   adapt->pEncodings = &DummyEncodingBlit;
   adapt->nFormats = NUM_FORMATS;
   adapt->pFormats = SISFormats;
   adapt->nImages = NUM_IMAGES_BLIT;
   adapt->pImages = SISImagesBlit;
   adapt->pAttributes = SISAttributes_Blit;
   adapt->nAttributes = NUM_ATTRIBUTES_BLIT;
   adapt->nPorts = NUM_BLIT_PORTS;
   adapt->pPortPrivates = (DevUnion*)(&adapt[1]);

   pSiS->blitPriv = (void *)(&adapt->pPortPrivates[NUM_BLIT_PORTS]);
   pPriv = (SISBPortPrivPtr)(pSiS->blitPriv);

   for(i = 0; i < NUM_BLIT_PORTS; i++) {
      adapt->pPortPrivates[i].uval = (ULong)(i);
#if defined(REGION_NULL)
      REGION_NULL(pScreen, &pPriv->blitClip[i]);
#else
      REGION_INIT(pScreen, &pPriv->blitClip[i], NullBox, 0);
#endif
      pPriv->videoStatus[i] = 0;
      pPriv->currentBuf[i]  = 0;
      pPriv->handle[i]      = NULL;
   }

   /* Scanline trigger not implemented by hardware! */
   pPriv->VBlankTriggerCRT1 = 0; /* SCANLINE_TRIGGER_ENABLE | SCANLINE_TR_CRT1; */
   pPriv->VBlankTriggerCRT2 = 0; /* SCANLINE_TRIGGER_ENABLE | SCANLINE_TR_CRT2; */
   if(pSiS->ChipType >= SIS_330) {
      pPriv->AccelCmd = YUVRGB_BLIT_330;
   } else {
      pPriv->AccelCmd = YUVRGB_BLIT_325;
   }

   adapt->PutVideo = NULL;
   adapt->PutStill = NULL;
   adapt->GetVideo = NULL;
   adapt->GetStill = NULL;
   adapt->StopVideo = (StopVideoFuncPtr)SISStopVideoBlit;
   adapt->SetPortAttribute = (SetPortAttributeFuncPtr)SISSetPortAttributeBlit;
   adapt->GetPortAttribute = (GetPortAttributeFuncPtr)SISGetPortAttributeBlit;
   adapt->QueryBestSize = (QueryBestSizeFuncPtr)SISQueryBestSizeBlit;
   adapt->PutImage = (PutImageFuncPtr)SISPutImageBlit;
   adapt->QueryImageAttributes = SISQueryImageAttributesBlit;

   pSiS->blitadaptor = adapt;

   pSiS->xvVSync = MAKE_ATOM(sisxvvsync);
   pSiS->xvSetDefaults = MAKE_ATOM(sisxvsetdefaults);

   SISResetVideoBlit(pScrn);

   /* Reset the properties to their defaults */
   SISSetPortDefaultsBlit(pScrn, pPriv);

   return adapt;
}

static int
SISGetPortAttributeBlit(ScrnInfoPtr pScrn, Atom attribute,
  			INT32 *value, ULong index)
{
   SISPtr pSiS = SISPTR(pScrn);
   SISBPortPrivPtr pPriv = (SISBPortPrivPtr)(pSiS->blitPriv);

   if(attribute == pSiS->xvVSync) {
      *value = pPriv->vsync;
   } else return BadMatch;
   return Success;
}

static int
SISSetPortAttributeBlit(ScrnInfoPtr pScrn, Atom attribute,
  		    	INT32 value, ULong index)
{
   SISPtr pSiS = SISPTR(pScrn);
   SISBPortPrivPtr pPriv = (SISBPortPrivPtr)(pSiS->blitPriv);

   if(attribute == pSiS->xvVSync) {
      if((value < 0) || (value > 1)) return BadValue;
      pPriv->vsync = value;
   } else if(attribute == pSiS->xvSetDefaults) {
      SISSetPortDefaultsBlit(pScrn, pPriv);
   } else return BadMatch;
   return Success;
}

static void
SISStopVideoBlit(ScrnInfoPtr pScrn, ULong index, Bool shutdown)
{
   SISPtr pSiS = SISPTR(pScrn);
   SISBPortPrivPtr pPriv = (SISBPortPrivPtr)(pSiS->blitPriv);

   /* This shouldn't be called for blitter adaptors due to
    * adapt->flags but we provide it anyway.
    */

   if(index >= NUM_BLIT_PORTS) return;

   REGION_EMPTY(pScrn->pScreen, &pPriv->blitClip[index]);

   if(shutdown) {
      (*pSiS->SyncAccel)(pScrn);
      pPriv->videoStatus[index] = 0;
      SISFreeFBMemory(pScrn, &pPriv->handle[(int)index]);
   }
}

static int
SISPutImageBlit(
  ScrnInfoPtr pScrn,
  short src_x, short src_y,
  short drw_x, short drw_y,
  short src_w, short src_h,
  short drw_w, short drw_h,
  int id, UChar *buf,
  short width, short height,
  Bool sync,
  RegionPtr clipBoxes, ULong index,
  DrawablePtr pDraw
){
   SISPtr pSiS = SISPTR(pScrn);
   SISBPortPrivPtr pPriv = (SISBPortPrivPtr)(pSiS->blitPriv);
#if XF86_VERSION_CURRENT < XF86_VERSION_NUMERIC(4,1,99,1,0)
   XAAInfoRecPtr pXAA = pSiS->AccelInfoPtr;
#endif
   BoxPtr pbox = REGION_RECTS(clipBoxes);
   int    nbox = REGION_NUM_RECTS(clipBoxes);
   CARD32 dstbase = 0, offsety, offsetuv, temp;
   int    totalSize, bytesize=0, h, w, wb, srcPitch;
   int 	  xoffset = 0, yoffset = 0, left, right, top, bottom;
   UChar  *ybases, *ubases = NULL, *vbases = NULL, *myubases, *myvbases;
   UChar  *ybased, *uvbased, packed;
   CARD16 *myuvbased;
   SiS_Packet12_YUV MyPacket;
   Bool first;

   if(index >= NUM_BLIT_PORTS) return BadMatch;

   if(!height || !width) return Success;

   switch(id) {
     case PIXEL_FMT_YV12:
     case PIXEL_FMT_I420:
     case PIXEL_FMT_NV12:
     case PIXEL_FMT_NV21:
       srcPitch = (width + 7) & ~7;  /* Should come this way anyway */
       bytesize = srcPitch * height;
       totalSize = (bytesize * 3) >> 1;
       break;
     case PIXEL_FMT_YUY2:
     case PIXEL_FMT_UYVY:
     case PIXEL_FMT_YVYU:
       srcPitch = ((width << 1) + 3) & ~3;
       /* Size = width * 2 * height */
       totalSize = srcPitch * height;
       bytesize = 0;
       break;
     default:
       return BadMatch;
   }

   /* allocate memory (we do doublebuffering) */
   if(!(pPriv->bufAddr[index][0] = SISAllocateFBMemory(pScrn, &pPriv->handle[index], totalSize << 1)))
      return BadAlloc;

   pPriv->bufAddr[index][1] = pPriv->bufAddr[index][0] + totalSize;

   if(drw_w > width) {
      xoffset = (drw_w - width) >> 1;
   }
   if(drw_h > (height & ~1)) {
      yoffset = (drw_h - height) >> 1;
   }

   if(xoffset || yoffset) {
#if XF86_VERSION_CURRENT < XF86_VERSION_NUMERIC(4,3,99,3,0)
      if(!RegionsEqual(&pPriv->blitClip[index], clipBoxes)) {
#else
      if(!REGION_EQUAL(pScrn->pScreen, &pPriv->blitClip[index], clipBoxes)) {
#endif
#if XF86_VERSION_CURRENT < XF86_VERSION_NUMERIC(4,1,99,1,0)
         (*pXAA->FillSolidRects)(pScrn, 0x00000000, GXcopy, ~0,
                              REGION_NUM_RECTS(clipBoxes),
                              REGION_RECTS(clipBoxes));
#else
         xf86XVFillKeyHelper(pScrn->pScreen, 0x00000000, clipBoxes);
#endif
         REGION_COPY(pScrn->pScreen, &pPriv->blitClip[index], clipBoxes);
      }
   }

   memset(&MyPacket, 0, sizeof(MyPacket));

   ybased = pSiS->FbBase + pPriv->bufAddr[index][pPriv->currentBuf[index]];
   uvbased = pSiS->FbBase + pPriv->bufAddr[index][pPriv->currentBuf[index]] + bytesize;

   ybases = buf;
   packed = 0;

   switch(id) {
     case PIXEL_FMT_YV12:
	vbases = buf + bytesize;
	ubases = buf + bytesize*5/4;
	break;
     case PIXEL_FMT_I420:
	ubases = buf + bytesize;
	vbases = buf + bytesize*5/4;
	break;
     case PIXEL_FMT_NV12:
        MyPacket.P12_Command = YUV_FORMAT_NV12;
        break;
     case PIXEL_FMT_NV21:
        MyPacket.P12_Command = YUV_FORMAT_NV21;
        break;
     case PIXEL_FMT_YUY2:
        MyPacket.P12_Command = YUV_FORMAT_YUY2;
	packed = 1;
        break;
     case PIXEL_FMT_UYVY:
        MyPacket.P12_Command = YUV_FORMAT_UYVY;
	packed = 1;
        break;
     case PIXEL_FMT_YVYU:
        MyPacket.P12_Command = YUV_FORMAT_YVYU;
	packed = 1;
        break;
     default:
        return BadMatch;
   }

   switch(id) {
   case PIXEL_FMT_YV12:
   case PIXEL_FMT_I420:
      MyPacket.P12_Command = YUV_FORMAT_NV12;
      /* Copy y plane */
      SiSMemCopyToVideoRam(pSiS, ybased, ybases, bytesize);
      /* Copy u/v planes */
      wb = srcPitch >> 1;
      h = height >> 1;
      while(h--) {
         myuvbased = (CARD16*)uvbased;
         myubases = ubases;
         myvbases = vbases;
	 w = wb;
	 while(w--) {
#if X_BYTE_ORDER == X_BIG_ENDIAN
 	    temp =  (*myubases++) << 8;
	    temp |= (*myvbases++);
#else
	    temp =  (*myvbases++) << 8;
	    temp |= (*myubases++);
#endif
	    *myuvbased++ = temp;
	 }
	 uvbased += srcPitch;
	 ubases += wb;
	 vbases += wb;
      }
      break;
   default:
      SiSMemCopyToVideoRam(pSiS, ybased, ybases, totalSize);
   }

   dstbase += FBOFFSET;

   MyPacket.P12_Header0 = SIS_PACKET12_HEADER0;
   MyPacket.P12_Header1 = SIS_PACKET12_HEADER1;
   MyPacket.P12_Null1 = SIS_NIL_CMD;
   MyPacket.P12_Null2 = SIS_NIL_CMD;
   MyPacket.P12_YPitch = MyPacket.P12_UVPitch = srcPitch;
   MyPacket.P12_DstAddr = dstbase;
   MyPacket.P12_DstPitch = pSiS->scrnOffset;
   MyPacket.P12_DstHeight = 0x0fff;

   MyPacket.P12_Command |= pPriv->AccelCmd		|
			   SRCVIDEO			|
			   PATFG			|
			   pSiS->SiS310_AccelDepth	|
			   YUV_CMD_YUV			|
			   DSTVIDEO;


#if 0 /* Not implemented by hardware! */
   if(pPriv->vsync) {
#ifdef SISMERGED
      if(!pSiS->MergedFB) {
#endif
#ifdef SISDUALHEAD
         if(pSiS->DualHeadMode) {
	    if(pSiS->SecondHead) {
	       MyPacket.P12_Command |= pPriv->VBlankTriggerCRT1;
	    } else {
	       MyPacket.P12_Command |= pPriv->VBlankTriggerCRT2;
	    }
	 } else {
#endif
            Bool IsSlaveMode = SiSBridgeIsInSlaveMode(pScrn);
            if((pSiS->VBFlags & DISPTYPE_DISP2) && !IsSlaveMode)
	       MyPacket.P12_Command |= pPriv->VBlankTriggerCRT2;
	    else if((pSiS->VBFlags & DISPTYPE_DISP1) || IsSlaveMode)
	       MyPacket.P12_Command |= pPriv->VBlankTriggerCRT1;
#ifdef SISDUALHEAD
         }
#endif
#ifdef SISMERGED
      }
#endif
   }
#endif

   first = TRUE;
   while(nbox--) {
      left = pbox->x1;
      if(left >= drw_x + xoffset + width) goto mycont;

      right = pbox->x2;
      if(right <= drw_x + xoffset) goto mycont;

      top = pbox->y1;
      if(top >= drw_y + yoffset + height) goto mycont;

      bottom = pbox->y2;
      if(bottom <= drw_y + yoffset) goto mycont;

      if(left < (drw_x + xoffset)) left = drw_x + xoffset;
      if(right > (drw_x + xoffset + width)) right = drw_x + xoffset + width;
      if(top < (drw_y + yoffset)) top = drw_y + yoffset;
      if(bottom > (drw_y + yoffset + height)) bottom = drw_y + yoffset + height;

      MyPacket.P12_DstX = left;
      MyPacket.P12_DstY = top;
      MyPacket.P12_RectWidth = right - left;
      MyPacket.P12_RectHeight = bottom - top;

#if 0
#ifdef SISMERGED
      if((first) && (pSiS->MergedFB)) {
         int scrwidth = ((SiSMergedDisplayModePtr)pSiS->CurrentLayout.mode->Private)->CRT2->HDisplay;
	 int scrheight = ((SiSMergedDisplayModePtr)pSiS->CurrentLayout.mode->Private)->CRT2->VDisplay;
	 if( (right < pSiS->CRT2pScrn->frameX0) ||
	     (left >= pSiS->CRT2pScrn->frameX0 + scrwidth) ||
	     (bottom < pSiS->CRT2pScrn->frameY0) ||
	     (top >= pSiS->CRT2pScrn->frameY0 + scrheight) ) {
	    MyPacket.P12_Command |= pPriv->VBlankTriggerCRT1;
	 } else {
	    MyPacket.P12_Command |= pPriv->VBlankTriggerCRT2;
	 }
      }
#endif
#endif

      offsety = offsetuv = 0;
      if(packed) {
         if(pbox->y1 > drw_y + yoffset) {
            offsetuv  = (pbox->y1 - drw_y - yoffset) * srcPitch;
         }
         if(pbox->x1 > drw_x + xoffset) {
            offsetuv += ((pbox->x1 - drw_x - xoffset) << 1);
	    if(offsetuv & 3) {
#if 0	       /* Paint over covering object - no */
	       if(MyPacket.P12_DstX > 0) {
	          offsetuv &= ~3;
	          MyPacket.P12_DstX--;
	          MyPacket.P12_RectWidth++;
	       } else {
#endif
	          offsetuv = (offsetuv + 3) & ~3;
	          MyPacket.P12_DstX++;
	          MyPacket.P12_RectWidth--;
#if 0
	       }
#endif
	    }
         }
      } else {
         if(pbox->y1 > drw_y + yoffset) {
            offsety  = (pbox->y1 - drw_y - yoffset) * srcPitch;
	    offsetuv = ((pbox->y1 - drw_y - yoffset) >> 1) * srcPitch;
         }
         if(pbox->x1 > drw_x + xoffset) {
            offsety += (pbox->x1 - drw_x - xoffset);
	    offsetuv += (pbox->x1 - drw_x - xoffset);
	    if(offsetuv & 1) {
	       offsety++;
	       offsetuv++;
	       MyPacket.P12_DstX++;
	       MyPacket.P12_RectWidth--;
	    }
         }
      }

      if(!MyPacket.P12_RectWidth) continue;

      MyPacket.P12_YSrcAddr  = pPriv->bufAddr[index][pPriv->currentBuf[index]] + offsety + FBOFFSET;
      MyPacket.P12_UVSrcAddr = pPriv->bufAddr[index][pPriv->currentBuf[index]] + bytesize + offsetuv + FBOFFSET;
      SISWriteBlitPacket(pSiS, (CARD32*)&MyPacket);
#if 0
      MyPacket.P12_Command &= ~(pPriv->VBlankTriggerCRT1 | pPriv->VBlankTriggerCRT2);
#endif
      first = FALSE;
mycont:
      pbox++;
   }

#if 0
   {
   int debug = 0;
   while( (SIS_MMIO_IN16(pSiS->IOBase, Q_STATUS+2) & 0x8000) != 0x8000) { debug++; };
   while( (SIS_MMIO_IN16(pSiS->IOBase, Q_STATUS+2) & 0x8000) != 0x8000) { debug++; };
   xf86DrvMsg(0, X_INFO, "vsync %d, debug %d\n", pPriv->vsync, debug);
   }
#endif

   pPriv->currentBuf[index] ^= 1;

   UpdateCurrentTime();
   pPriv->freeTime[index] = currentTime.milliseconds + FREE_DELAY;
   pPriv->videoStatus[index] = FREE_TIMER;

   pSiS->VideoTimerCallback = SISVideoTimerCallback;

   return Success;
}

static int
SISQueryImageAttributesBlit(
  ScrnInfoPtr pScrn,
  int id,
  UShort *w, UShort *h,
  int *pitches, int *offsets
){
    int    pitchY, pitchUV;
    int    size, sizeY, sizeUV;

    if(*w > DummyEncodingBlit.width) *w = DummyEncodingBlit.width;
    if(*h > DummyEncodingBlit.height) *h = DummyEncodingBlit.height;

    switch(id) {
    case PIXEL_FMT_YV12:
    case PIXEL_FMT_I420:
	*w = (*w + 7) & ~7;
	*h = (*h + 1) & ~1;
	pitchY = *w;
	pitchUV = *w >> 1;
	if(pitches) {
	   pitches[0] = pitchY;
	   pitches[1] = pitches[2] = pitchUV;
	}
	sizeY = pitchY * (*h);
	sizeUV = pitchUV * ((*h) >> 1);
	if(offsets) {
	   offsets[0] = 0;
	   offsets[1] = sizeY;
	   offsets[2] = sizeY + sizeUV;
	}
	size = sizeY + (sizeUV << 1);
	break;
    case PIXEL_FMT_NV12:
    case PIXEL_FMT_NV21:
	*w = (*w + 7) & ~7;
	pitchY = *w;
	pitchUV = *w;
	if(pitches) {
	   pitches[0] = pitchY;
	   pitches[1] = pitchUV;
	}
	sizeY = pitchY * (*h);
	sizeUV = pitchUV * ((*h) >> 1);
	if(offsets) {
	   offsets[0] = 0;
	   offsets[1] = sizeY;
	}
	size = sizeY + (sizeUV << 1);
	break;
    case PIXEL_FMT_YUY2:
    case PIXEL_FMT_UYVY:
    case PIXEL_FMT_YVYU:
    default:
	*w = (*w + 1) & ~1;
	pitchY = *w << 1;
	if(pitches) pitches[0] = pitchY;
	if(offsets) offsets[0] = 0;
	size = pitchY * (*h);
	break;
    }

    return size;
}

static void
SISQueryBestSizeBlit(
  ScrnInfoPtr pScrn,
  Bool motion,
  short vid_w, short vid_h,
  short drw_w, short drw_h,
  unsigned int *p_w, unsigned int *p_h,
  ULong index
){
  /* We cannot scale */
  *p_w = vid_w;
  *p_h = vid_h;
}
#endif /* INCL_YUV */

/*****************************************/
/*            TIMER CALLBACK             */
/*****************************************/

static void
SISVideoTimerCallback(ScrnInfoPtr pScrn, Time now)
{
    SISPtr          pSiS = SISPTR(pScrn);
    SISPortPrivPtr  pPriv = NULL;
#ifdef INCL_YUV_BLIT_ADAPTOR
    SISBPortPrivPtr pPrivBlit = NULL;
#endif
    UChar           sridx, cridx;
    Bool	    setcallback = FALSE;

    if(!pScrn->vtSema) return;

    if(pSiS->adaptor) {
       pPriv = GET_PORT_PRIVATE(pScrn);
       if(!pPriv->videoStatus) pPriv = NULL;
    }

    if(pPriv) {
       if(pPriv->videoStatus & TIMER_MASK) {
          if(pPriv->videoStatus & OFF_TIMER) {
	     setcallback = TRUE;
	     if(pPriv->offTime < now) {
                /* Turn off the overlay */
	        sridx = inSISREG(SISSR); cridx = inSISREG(SISCR);
                close_overlay(pSiS, pPriv);
	        outSISREG(SISSR, sridx); outSISREG(SISCR, cridx);
	        pPriv->mustwait = 1;
                pPriv->videoStatus = FREE_TIMER;
                pPriv->freeTime = now + FREE_DELAY;
	     }
          } else if(pPriv->videoStatus & FREE_TIMER) {
	     if(pPriv->freeTime < now) {
                SISFreeFBMemory(pScrn, &pPriv->handle);
	        pPriv->mustwait = 1;
                pPriv->videoStatus = 0;
             } else {
	        setcallback = TRUE;
	     }
          }
       }
    }

#ifdef INCL_YUV_BLIT_ADAPTOR
    if(pSiS->blitadaptor) {
       int i;
       pPrivBlit = (SISBPortPrivPtr)(pSiS->blitPriv);
       for(i = 0; i < NUM_BLIT_PORTS; i++) {
          if(pPrivBlit->videoStatus[i] & FREE_TIMER) {
	     if(pPrivBlit->freeTime[i] < now) {
                SISFreeFBMemory(pScrn, &pPrivBlit->handle[i]);
                pPrivBlit->videoStatus[i] = 0;
	     } else {
	        setcallback = TRUE;
	     }
          }
       }
    }
#endif

    pSiS->VideoTimerCallback = (setcallback) ? SISVideoTimerCallback : NULL;
}

