/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/apm/apm_video.c,v 1.10 2003/04/23 21:51:26 tsi Exp $ */

#if PSZ != 24
#include "dixstruct.h"

/*
 * Ported from mga_video.c by Loïc Grenié
 */

#ifndef OFF_DELAY
#define OFF_DELAY	200
#endif

static XF86VideoAdaptorPtr A(SetupImageVideo)(ScreenPtr);

static void	A(StopVideo)(ScrnInfoPtr, pointer, Bool);
static int	A(SetPortAttribute)(ScrnInfoPtr, Atom, INT32, pointer);
#ifndef IOP_ACCESS
static int	ApmGetPortAttribute(ScrnInfoPtr, Atom ,INT32 *, pointer);
static void	ApmQueryBestSize(ScrnInfoPtr, Bool, short, short, short,
				    short, unsigned int *, unsigned int *,
				    pointer);
static int	ApmQueryImageAttributes(ScrnInfoPtr, int,
					    unsigned short *, unsigned short *,
					    int *, int *);
#endif
static int	A(ReputImage)(ScrnInfoPtr, short, short, RegionPtr, pointer);
static int	A(PutImage)(ScrnInfoPtr, short, short, short, short, short,
				short, short, short, int, unsigned char*,
				short, short, Bool, RegionPtr, pointer);

static void	A(ResetVideo)(ScrnInfoPtr);
static void	A(XvMoveCB)(FBAreaPtr, FBAreaPtr);
static void	A(XvRemoveCB)(FBAreaPtr);

#define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, TRUE)

void A(InitVideo)(ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    XF86VideoAdaptorPtr *adaptors, *newAdaptors;
    XF86VideoAdaptorPtr newAdaptor;
    APMDECL(pScrn);
    int num_adaptors;
    Bool freeAdaptors = FALSE;

    num_adaptors = xf86XVListGenericAdaptors(pScrn, &adaptors);

    if (pApm->Chipset >= AT24) {
	if ((newAdaptor = A(SetupImageVideo)(pScreen))) {

	   newAdaptors = xalloc((num_adaptors + 1) *
				   sizeof(XF86VideoAdaptorPtr*));
	   if(newAdaptors) {
		if(num_adaptors)
		    memcpy(newAdaptors, adaptors, num_adaptors *
					sizeof(XF86VideoAdaptorPtr));
		newAdaptors[num_adaptors] = newAdaptor;
		adaptors = newAdaptors;
		num_adaptors++;
		freeAdaptors = TRUE;
	   }
	}
    }

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

    if(freeAdaptors)
	xfree(adaptors);
}

#ifndef APM_VIDEO_DEFINES
#define APM_VIDEO_DEFINES

static Atom xvBrightness, xvContrast;

/* client libraries expect an encoding */
static XF86VideoEncodingRec DummyEncoding[1] =
{
    {
	0,
	"XV_IMAGE",
	1024, 1024,
	{1, 1}
    }
};

#define NUM_FORMATS 24

static XF86VideoFormatRec Formats[NUM_FORMATS] =
{
    { 8, PseudoColor},
    {15, PseudoColor},
    {16, PseudoColor},
    {24, PseudoColor},
    {32, PseudoColor},
    { 8, DirectColor},
    {15, DirectColor},
    {16, DirectColor},
    {24, DirectColor},
    {32, DirectColor},
    { 8,   TrueColor},
    {15,   TrueColor},
    {16,   TrueColor},
    {24,   TrueColor},
    {32,   TrueColor}
};

#define NUM_ATTRIBUTES 2

static XF86AttributeRec Attributes[NUM_ATTRIBUTES] =
{
    {XvSettable | XvGettable, -128, 127, "XV_BRIGHTNESS"},
    {XvSettable | XvGettable, 0, 255, "XV_CONTRAST"}
};

#define NUM_IMAGES 8
typedef char c8;

static XF86ImageRec Images[NUM_IMAGES] =
{
   {
	0x35315652,
        XvRGB,
	LSBFirst,
	{'R','V','1','5',
	  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
	16,
	XvPacked,
	1,
	15, 0x001F, 0x03E0, 0x7C00,
	0, 0, 0,
	0, 0, 0,
	0, 0, 0,
	{'R','V','B',0,
	  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
	XvTopToBottom
   },
   {
	0x36315652,
        XvRGB,
	LSBFirst,
	{'R','V','1','6',
	  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
	16,
	XvPacked,
	1,
	16, 0x001F, 0x07E0, 0xF800,
	0, 0, 0,
	0, 0, 0,
	0, 0, 0,
	{'R','V','B',0,
	  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
	XvTopToBottom
   },
   {
	0x32335652,
        XvRGB,
	LSBFirst,
	{'R','V','3','2',
	  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
	32,
	XvPacked,
	1,
	24, 0x0000FF, 0x00FF00, 0xFF0000,
	0, 0, 0,
	0, 0, 0,
	0, 0, 0,
	{'R','V','B',0,
	  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
	XvTopToBottom
   },
   {
	0x59595959,
        XvYUV,
	LSBFirst,
	{0x00,0x00,0x00,0x00,
	  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
	8,
	XvPacked,
	1,
	0, 0, 0, 0,
	8, 0, 0,
	1, 1, 1,
	1, 1, 1,
	{'Y','Y','Y','Y',
	  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
	XvTopToBottom
   },
   {
	0x32315659,
        XvYUV,
	LSBFirst,
	{'Y','V','1','2',
	  0x00,0x00,0x00,0x10,(c8)0x80,0x00,0x00,(c8)0xAA,0x00,0x38,(c8)0x9B,0x71},
	12,
	XvPlanar,
	3,
	0, 0, 0, 0 ,
	8, 8, 8,
	1, 2, 2,
	1, 2, 2,
	{'Y','V','U',
	  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
	XvTopToBottom
   },
   {
	0x59565955,
        XvYUV,
	LSBFirst,
	{'U','Y','V','Y',
	  0x00,0x00,0x00,0x10,(c8)0x80,0x00,0x00,(c8)0xAA,0x00,0x38,(c8)0x9B,0x71},
	16,
	XvPlanar,
	1,
	0, 0, 0, 0,
	8, 8, 8,
	1, 2, 2,
	1, 1, 1,
	{'U','Y','V','Y',
	  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
	XvTopToBottom
   },
   {
	0x55595659,
        XvYUV,
	LSBFirst,
	{'Y','V','Y','U',
	  0x00,0x00,0x00,0x10,(c8)0x80,0x00,0x00,(c8)0xAA,0x00,0x38,(c8)0x9B,0x71},
	16,
	XvPlanar,
	1,
	0, 0, 0, 0,
	8, 8, 8,
	1, 2, 2,
	1, 1, 1,
	{'Y','V','Y','U',
	  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
	XvTopToBottom
   },
   {
	0x59555956,
        XvYUV,
	LSBFirst,
	{'V','Y','U','Y',
	  0x00,0x00,0x00,0x10,(c8)0x80,0x00,0x00,(c8)0xAA,0x00,0x38,(c8)0x9B,0x71},
	16,
	XvPlanar,
	1,
	0, 0, 0, 0,
	8, 8, 8,
	1, 2, 2,
	1, 1, 1,
	{'V','Y','U','Y',
	  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
	XvTopToBottom
   }
};

typedef struct {
   Bool		on;
   unsigned char	brightness;
   unsigned char	contrast;
   unsigned short	reg, val;
   ApmPtr	pApm;
   int		x1, x10, y1, drw_x, drw_y, Bpp, Bps;
   FBAreaPtr	area;
   RegionRec	clip;
   int		xnum, xden, ynum, yden;
   CARD32	scalex, scaley;
   CARD32	data;
} ApmPortPrivRec, *ApmPortPrivPtr;
#endif


static void
A(ResetVideo)(ScrnInfoPtr pScrn)
{
    APMDECL(pScrn);

    A(WaitForFifo)(pApm, 2);
    ((ApmPortPrivPtr)pApm->adaptor->pPortPrivates[0].ptr)->on = 0;
    ((ApmPortPrivPtr)pApm->adaptor->pPortPrivates[1].ptr)->on = 0;
    WRXW(0x82, 0);
    WRXW(0x92, 0);
}


static XF86VideoAdaptorPtr
A(SetupImageVideo)(ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    APMDECL(pScrn);
    XF86VideoAdaptorPtr adapt;
    ApmPortPrivPtr pPriv;

    if(!(adapt = xcalloc(1, sizeof(XF86VideoAdaptorRec) +
			    2 * sizeof(ApmPortPrivRec) +
			    2 * sizeof(DevUnion))))
	return NULL;

    adapt->type = XvWindowMask | XvInputMask | XvImageMask;
    adapt->flags = VIDEO_OVERLAID_IMAGES;
    adapt->name = "Alliance Pro Motion video engine";
    adapt->nEncodings = 1;
    adapt->pEncodings = DummyEncoding;
    adapt->nFormats = NUM_FORMATS;
    adapt->pFormats = Formats;
    adapt->nPorts = 2;
    adapt->pPortPrivates = (DevUnion*)(&adapt[1]);
    pPriv = (ApmPortPrivPtr)(&adapt->pPortPrivates[2]);
    pPriv->pApm = pApm;
    pPriv[1].pApm = pApm;
    pPriv->reg = 0x82;
    pPriv[1].reg = 0x92;
    adapt->pPortPrivates[0].ptr = (pointer)(pPriv);
    adapt->pPortPrivates[1].ptr = (pointer)(pPriv + 1);
    adapt->nAttributes = NUM_ATTRIBUTES;
    adapt->pAttributes = Attributes;
    adapt->nImages = NUM_IMAGES;
    adapt->pImages = Images;
    adapt->PutVideo = NULL;
    adapt->PutStill = NULL;
    adapt->GetVideo = NULL;
    adapt->GetStill = NULL;
    adapt->StopVideo = A(StopVideo);
    adapt->SetPortAttribute = A(SetPortAttribute);
    adapt->GetPortAttribute = ApmGetPortAttribute;
    adapt->QueryBestSize = ApmQueryBestSize;
    adapt->PutImage = A(PutImage);
    adapt->ReputImage = A(ReputImage);
    adapt->QueryImageAttributes = ApmQueryImageAttributes;

    pPriv->brightness = 0;
    pPriv->contrast = 128;
    pPriv[1].brightness = 0;
    pPriv[1].contrast = 128;

    /* gotta uninit this someplace */
    REGION_INIT(pScreen, &pPriv->clip, NullBox, 0);
    REGION_INIT(pScreen, &(pPriv + 1)->clip, NullBox, 0);

    pApm->adaptor = adapt;

    xvBrightness = MAKE_ATOM("XV_BRIGHTNESS");
    xvContrast   = MAKE_ATOM("XV_CONTRAST");

    A(ResetVideo)(pScrn);

    return adapt;
}

#ifndef IOP_ACCESS
/* ApmClipVideo -

   Takes the dst box in standard X BoxRec form (top and left
   edges inclusive, bottom and right exclusive).  The new dst
   box is returned.  The source boundaries are given (x1, y1
   inclusive, x2, y2 exclusive) and returned are the new source
   boundaries in 16.16 fixed point.

  extents is the extents of the clip region
*/

static void
ApmClipVideo(BoxPtr dst, INT32 *x1, INT32 *x2, INT32 *y1, INT32 *y2,
	      BoxPtr extents, INT32 width, INT32 height,
	      CARD32 *scalex, CARD32 *scaley, INT32 mask)
{
    INT32 vscale, hscale;
    int diff;

    if (dst->x2 - dst->x1 < *x2 - *x1)
	dst->x2 = dst->x1 + *x2 - *x1;

    if (dst->y2 - dst->y1 < *y2 - *y1)
	dst->y2 = dst->y1 + *y2 - *y1;

    *x1 <<= 12; *x2 <<= 16;
    *y1 <<= 12; *y2 <<= 16;

    hscale = (*x2 - *x1) / (dst->x2 - dst->x1);
    vscale = (*y2 - *y1) / (dst->y2 - dst->y1);

    diff = extents->x1 - dst->x1;
    if(diff > 0) {
	dst->x1 = extents->x1;
	*x1 += diff * hscale;
    }
    diff = dst->x2 - extents->x2;
    if(diff > 0) {
	dst->x2 = extents->x2;
	*x2 -= diff * hscale;
    }
    diff = extents->y1 - dst->y1;
    if(diff > 0) {
	dst->y1 = extents->y1;
	*y1 += diff * vscale;
    }
    diff = dst->y2 - extents->y2;
    if(diff > 0) {
	dst->y2 = extents->y2;
	*y2 -= diff * vscale;
    }

    if (*x2 - *x1 == 0x10000 * (dst->x2 - dst->x1))	/* Shrinking */
	*scalex = 0;
    else
	*scalex = ((*x2 - *x1) / (dst->x2 - dst->x1)) >> 4;
    if (*y2 - *y1 == 0x10000 * (dst->y2 - dst->y1))	/* Shrinking */
	*scaley = 0;
    else
	*scaley = ((*y2 - *y1) / (dst->y2 - dst->y1)) >> 4;
}
#endif

static void
A(StopVideo)(ScrnInfoPtr pScrn, pointer data, Bool shutdown)
{
    ApmPortPrivPtr pPriv = (ApmPortPrivPtr)data;
    APMDECL(pScrn);

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

    pPriv->on = 0;
    A(WaitForFifo)(pApm, 1);
    WRXB(pPriv->reg, 0);
}

static int
A(SetPortAttribute)(ScrnInfoPtr pScrn, Atom attribute, INT32 value,
		      pointer data)
{
  ApmPortPrivPtr pPriv = (ApmPortPrivPtr)data;
  /*APMDECL(pScrn);*/

  if(attribute == xvBrightness) {
	if((value < -128) || (value > 127))
	   return BadValue;
	pPriv->brightness = value;
	/* TODO : enable */
  } else if(attribute == xvContrast) {
	if((value < 0) || (value > 255))
	   return BadValue;
	pPriv->contrast = value;
	/* TODO : enable */
  }

  return Success;
}

#ifndef IOP_ACCESS
static int
ApmGetPortAttribute(ScrnInfoPtr pScrn, Atom attribute, INT32 *value,
		      pointer data)
{
  ApmPortPrivPtr pPriv = (ApmPortPrivPtr)data;

  if(attribute == xvBrightness) {
	*value = pPriv->brightness;
  } else
  if(attribute == xvContrast) {
	*value = pPriv->contrast;
  }

  return Success;
}

static void
ApmQueryBestSize(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)
{
    APMDECL(pScrn);
    unsigned short	round = ~pApm->CurrentLayout.mask32;

    *p_w = drw_w & round;
    *p_h = drw_h & round;
}
#endif

static void A(XvMoveCB)(FBAreaPtr area1, FBAreaPtr area2)
{
    ApmPortPrivPtr	pPriv = (ApmPortPrivPtr)area1->devPrivate.ptr;
    ApmPtr		pApm = pPriv->pApm;

    pPriv->on = 0;
    A(WaitForFifo)(pApm, 1);
    WRXB(pPriv->reg, 0);	/* Stop video for this port */
    pPriv->area = area2;
}

static void A(XvRemoveCB)(FBAreaPtr area)
{
    ApmPortPrivPtr	pPriv = (ApmPortPrivPtr)area->devPrivate.ptr;
    ApmPtr		pApm = pPriv->pApm;

    pPriv->on = 0;
    A(WaitForFifo)(pApm, 1);
    WRXB(pPriv->reg, 0);	/* Stop video for this port */
    pPriv->area = NULL;
}

static int
A(ReputImage)(ScrnInfoPtr pScrn, short drw_x, short drw_y,
		RegionPtr clipBoxes, pointer pdata)
{
    APMDECL(pScrn);
    ApmPortPrivPtr	pPriv = pdata, pPriv0, pPriv1;
    register int	fx, fy;
    CARD32	mask;
    RegionRec	Union;
    RegionPtr	reg0;
    int		nrects, CurrY, tile;
    int		X1, X2, Y1, y2, xmax, ymax;
    BoxPtr	rects;
    Bool	didit = 0;

    mask = pApm->CurrentLayout.mask32;
    fx = pScrn->frameX0 & ~mask;
    fy = pScrn->frameY0 + 1;
    REGION_COPY(pScreen, &pPriv->clip, clipBoxes);
    pPriv->x1 += drw_x - pPriv->drw_x;
    pPriv->x10 = ((pPriv->x1 + mask) & ~mask) - fx;
    pPriv->y1 += drw_y - pPriv->drw_y;
    pPriv->drw_x = drw_x;
    pPriv->drw_y = drw_y;
    A(WaitForFifo)(pApm, 2);
    WRXW(pPriv->reg + 0x06, 0xFFF - ((pPriv->scalex * pPriv->x10) & 0xFFF));
    WRXW(pPriv->reg + 0x0A, 0xFFF - ((pPriv->scaley * pPriv->y1) & 0xFFF));
    pPriv0 = (ApmPortPrivPtr)pApm->adaptor->pPortPrivates[0].ptr;
    pPriv1 = (ApmPortPrivPtr)pApm->adaptor->pPortPrivates[1].ptr;
    reg0 = &pPriv0->clip;
    bzero(&Union, sizeof Union);
    REGION_EMPTY(pScreen, &Union);
    REGION_INIT(pScreen, &Union, NullBox, 0);
    REGION_UNION(pScreen, &Union, reg0, &pPriv1->clip);
    nrects = REGION_NUM_RECTS(&Union);
    rects = REGION_RECTS(&Union);
    tile = 0x200;
    xmax = pScrn->frameX1 - pScrn->frameX0 + 1;
    ymax = pScrn->frameY1 - pScrn->frameY0;
    CurrY = -1;
    goto BEGIN_LOOP_1;
    do {
	rects++;
BEGIN_LOOP_1:
	X1 = ((rects->x1 + mask) & ~mask) - fx;
	if (X1 < 0)
	    X1 = 0;
	X2 = (rects->x2 & ~mask) - fx;
	if (X2 > xmax)
	    X2 = xmax;
	y2 = rects->y2 - fy;
    } while ((X2 <= X1 || y2 < -1) && --nrects > 0);
    Y1 = rects->y1 - fy;

    while (!(STATUS() & 0x800));
    while (STATUS() & 0x800);
    while (nrects-- > 0) {
	CARD32	reg, data;
	int	x1, x2, y1;

	x1 = X1;
	x2 = X2;
	y1 = Y1;
	if (y1 < -1) y1 = -1;
	if (y1 > ymax)
	    break;
	didit = 1;
	if (y1 > CurrY) {
	    A(WaitForFifo)(pApm, 3);
	    WRXL(tile + 0x00, 0xFFF0011);
	    WRXL(tile + 0x04, y1 << 16);
	    WRXL(tile + 0x08, 0);
	    tile += 16;
	}
	if (RECT_IN_REGION(pScreen, reg0, rects)) {
	    pPriv = pPriv0;
	    reg = (x1 << 16) | 1;
	}
	else {
	    pPriv = pPriv1;
	    reg = (x1 << 16) | 2;
	}
	CurrY = y2;
	if (nrects <= 0)
	    goto BEGIN_LOOP_2;
	do {
	    rects++;
BEGIN_LOOP_2:
	    X1 = ((rects->x1 + mask) & ~mask) - fx;
	    if (X1 < 0)
		X1 = 0;
	    X2 = (rects->x2 & ~mask) - fx;
	    if (X2 > xmax)
		X2 = xmax;
	} while (X2 <= X1 && --nrects > 0);
	Y1 = rects->y1 - fy;
	y2 = rects->y2 - fy;
	data = pPriv->data + (((x1 - pPriv->x10)
				* pPriv->xden) / pPriv->xnum) * pPriv->Bpp +
	    (((y1 - pPriv->y1 + fy) * pPriv->yden) / pPriv->ynum) * pPriv->Bps;
	A(WaitForFifo)(pApm, 4);
	if (!nrects || tile == 0x2B0 || y1 < Y1) {
	    WRXL(tile   , 0x10 | reg);
	}
	else {
	    WRXL(tile   , reg);
	}
	WRXL(tile + 0x04, x2 | (CurrY << 16));
	WRXL(tile + 0x08, (((x2-x1)*pPriv->xden+pPriv->xnum-1) / pPriv->xnum) |
				(data << 16));
	WRXB(tile + 0x0C, data >> 16);
	tile += 16;
	if (tile == 0x2C0) {
	    tile = 0x200;
	    break;
	}
    }
    REGION_UNINIT(pScreen, &Union);

    if (didit) {
	A(WaitForFifo)(pApm, 1);
	WRXW(0x8E, tile - 0x200);
    }

    if (didit ^ ((pPriv0->val | pPriv1->val) & 1)) {
	if (didit) {
	    pPriv0->val |= 1;
	    pPriv1->val |= 1;
	}
	else {
	    pPriv0->val &= 0xFFFE;
	    pPriv1->val &= 0xFFFE;
	}
	if (pPriv0->on) {
	    A(WaitForFifo)(pApm, 1);
	    WRXW(0x82, pPriv0->val);
	}
	if (pPriv1->on) {
	    A(WaitForFifo)(pApm, 1);
	    WRXW(0x92, pPriv1->val);
	}
    }

    return Success;
}

static int
A(PutImage)(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, unsigned char* buf,
	      short width, short height, Bool sync, RegionPtr clipBoxes,
	      pointer data)
{
    ApmPortPrivPtr	pPriv = (ApmPortPrivPtr)data;
    ScreenPtr	pScreen = pScrn->pScreen;
    APMDECL(pScrn);
    INT32	x1, x2, y1, y2;
    unsigned char	*dst_start;
    int		pitch, Bpp, new_h, offset = 0, offset2 = 0, offset3 = 0;
    CARD32	mask;
    FBAreaPtr	area;
    int		srcPitch, dstPitch, srcPitch2 = 0;
    int		top, left, npixels, nlines;
    BoxRec	dstBox;
    CARD32	scalex, scaley, scale;
    CARD32	tmp;
    Bool	offscreen;

    offscreen = (buf < (unsigned char *)pApm->FbBase ||
		    buf > (unsigned char *)pApm->FbBase + 0x400000);

    if(drw_w > 16384) drw_w = 16384;

    /* Clip */
    x1 = src_x;
    x2 = src_x + src_w;
    y1 = src_y;
    y2 = src_y + src_h;

    dstBox.x1 = drw_x;
    dstBox.x2 = drw_x + drw_w;
    dstBox.y1 = drw_y;
    dstBox.y2 = drw_y + drw_h;

    mask = pApm->CurrentLayout.mask32;

    ApmClipVideo(&dstBox, &x1, &x2, &y1, &y2,
		REGION_EXTENTS(pScreen, clipBoxes), width, height,
		&scalex, &scaley, mask);

    pPriv->drw_x = drw_x;
    pPriv->drw_y = drw_y;
    pPriv->xnum = drw_w;
    if (scalex)
	pPriv->xden = src_w;
    else
	pPriv->xden = drw_w;	/* If image is larger than window */
    pPriv->ynum = drw_h;
    if (scaley)
	pPriv->yden = src_h;
    else
	pPriv->yden = drw_h;
    if((x1 - x2 >= 0xFFFF) || (y1 - y2 >= 0xFFFF))
     return Success;

    Bpp = pScrn->bitsPerPixel >> 3;
    pitch = Bpp * pScrn->displayWidth;

    switch(id) {
    case 0x32315659:
	dstPitch = ((width << 1) + 3) & ~3;
	srcPitch = (width + 3) & ~3;
	offset2 = srcPitch * height;
	srcPitch2 = ((width >> 1) + 3) & ~3;
	offset = srcPitch2 * (height >> 1);
	offset3 = offset + offset2;
	new_h = (2 * offset2 + pitch - 1) / pitch;
	break;
    case 0x59595959:
	srcPitch = width;
	dstPitch = (srcPitch + 3) & ~3;
	offset = dstPitch * height;
	new_h = (offset + pitch - 1) / pitch;
	break;
    case 0x32335652:
	srcPitch = (width << 2);
	dstPitch = (srcPitch + 3) & ~3;
	offset = dstPitch * height;
	new_h = (offset + pitch - 1) / pitch;
	break;
    default:
	if (pApm->PutImageStride)
	    srcPitch = pApm->PutImageStride;
	else
	    srcPitch = (width << 1);
	dstPitch = (srcPitch + 3) & ~3;
	offset = dstPitch * height;
	new_h = (offset + pitch - 1) / pitch;
	break;
    }

    area = pPriv->area;

    /* Allocate offscreen memory */
    if (offscreen && (!area || ((area->box.y2 - area->box.y1) < new_h))) {
	Bool nukeMem = FALSE;
	int max_w, max_h;

	xf86QueryLargestOffscreenArea(pScreen, &max_w, &max_h, 0,
			  FAVOR_WIDTH_THEN_AREA, PRIORITY_LOW);
	if (max_w == pScrn->displayWidth && max_h >= new_h) {
	    area = xf86AllocateOffscreenArea(pScreen,
					pScrn->displayWidth, new_h,
					4, A(XvMoveCB), A(XvRemoveCB), pPriv);
	    if (area) {
		if (pPriv->area)
		    xf86FreeOffscreenArea(pPriv->area);
	    }
	    else
		area = pPriv->area;	/* Should not happen */
	}
	if(!area) {
	    if(!(area = xf86AllocateOffscreenArea(pScreen,
					pScrn->displayWidth, new_h, 4,
					A(XvMoveCB), A(XvRemoveCB), pPriv)))
	    {
	       nukeMem = TRUE;
	    }
	} else {
	    if(!xf86ResizeOffscreenArea(area, pScrn->displayWidth, new_h)) {
	       xf86FreeOffscreenArea(area);
	       pPriv->area = area = NULL;
	       nukeMem = TRUE;
	    }
	}
	if(nukeMem) {
	    xf86QueryLargestOffscreenArea(pScreen, &max_w, &max_h, 0,
			    FAVOR_WIDTH_THEN_AREA, PRIORITY_EXTREME);

	    if((max_w < pScrn->displayWidth) || (max_h < new_h))
		return BadAlloc;

	    xf86PurgeUnlockedOffscreenAreas(pScreen);

	    area = xf86AllocateOffscreenArea(pScreen,
					pScrn->displayWidth, new_h, 4,
					A(XvMoveCB), A(XvRemoveCB), pPriv);
	}

	pPriv->area = area;
    }

    /* copy data */
    pPriv->x1 = dstBox.x1 /*drw_x*/;
    pPriv->y1 = dstBox.y1 /*drw_y*/;
    top = y1 >> 16;
    left = (x1 >> 16) & ~1;
    npixels = ((((x2 + 0xffff) >> 16) + 1) & ~1) - left;

    switch(id) {
    case 0x59595959:
	pPriv->Bpp = 1;
	break;
    default:
	pPriv->Bpp = 2;
	left <<= 1;
	break;
    case 0x32335652:
	pPriv->Bpp = 4;
	left <<= 2;
	break;
    }
    pPriv->Bps = pPriv->Bpp * pPriv->xden;
    if (offscreen) {
	offset = (area->box.y1 * pitch) + (top * dstPitch);
	dst_start = ((unsigned char *)pApm->FbBase) +
						(pPriv->data = offset + left);
	switch(id) {
	case 0x32315659:
	    top &= ~1;
	    tmp = ((top >> 1) * srcPitch2) + (left >> 2);
	    offset2 += tmp;
	    offset3 += tmp;
	    nlines = ((((y2 + 0xffff) >> 16) + 1) & ~1) - top;
	    xf86XVCopyYUV12ToPacked(buf + (top * srcPitch) + (left >> 1),
				    buf + offset2, buf + offset3, dst_start,
				    srcPitch, srcPitch2, dstPitch,
				    nlines, npixels);
	    break;
	default:
	    if (id == 0x32335652)
		npixels <<= 1;
	    else if (id == 0x59595959)
		npixels >>= 1;
	    buf += (top * srcPitch) + left;
	    nlines = ((y2 + 0xffff) >> 16) - top;
	    if (offscreen)
		xf86XVCopyPacked(buf, dst_start, srcPitch, dstPitch,
				 nlines, npixels);
	    break;
	}
    }
    else
	pPriv->data = buf - (unsigned char *)pApm->FbBase;
    pPriv->on = 1;
    A(WaitForFifo)(pApm, 3);
    WRXW(pPriv->reg + 0x02, dstPitch >> 2);
    WRXW(pPriv->reg + 0x04, scalex);
    WRXW(pPriv->reg + 0x08, scaley);
    pPriv->scalex = scalex;
    pPriv->scaley = scaley;
    if (scalex && scaley)
	scale = 0x0E00;
    else if (scalex)
	scale = 0x0600;
    else if (scaley)
	scale = 0x0A00;
    else
	scale = 0;
    switch(id) {
    case 0x59595959:
	pPriv->val = 0x017B | scale;
	break;
    case 0x32335652:
	pPriv->val = 0x002F | (scale & 0xF7FF);/*Smoothing filter doesn't work*/
	break;
    case 0x36315652:
	pPriv->val = 0x002B | (scale & 0xF7FF);
	break;
    case 0x35315652:
	pPriv->val = 0x0029 | (scale & 0xF7FF);
	break;
    case 0x59555956:
	pPriv->val = 0x013B | scale;
	break;
    case 0x55595659:
	pPriv->val = 0x014B | scale;
	break;
    case 0x32315659:
    case 0x59565955:
    default:
	pPriv->val = 0x016B | scale;
	break;
    }

    (void) A(ReputImage)(pScrn, drw_x, drw_y, clipBoxes, data);

    A(WaitForFifo)(pApm, 1);
    WRXW(pPriv->reg, pPriv->val);

    return Success;
}

#ifndef IOP_ACCESS
static int
ApmQueryImageAttributes(ScrnInfoPtr pScrn, int id,
			  unsigned short *w, unsigned short *h,
			  int *pitches, int *offsets)
{
    int size, tmp;

    if(*w > 1024) *w = 1024;
    if(*h > 1024) *h = 1024;

    *w = (*w + 1) & ~1;
    if(offsets) offsets[0] = 0;

    switch(id) {
    case 0x32315659:
	*h = (*h + 1) & ~1;
	size = (*w + 3) & ~3;
	if(pitches) pitches[0] = size;
	size *= *h;
	if(offsets) offsets[1] = size;
	tmp = ((*w >> 1) + 3) & ~3;
	if(pitches) pitches[1] = pitches[2] = tmp;
	tmp *= (*h >> 1);
	size += tmp;
	if(offsets) offsets[2] = size;
	size += tmp;
	break;
    case 0x59565955:
    case 0x55595659:
    case 0x59555956:
	size = *w << 1;
	goto common;
    case 0x59595959:
    default:
	size = *w;
common:
	if (pitches)
	    pitches[0] = size;
	size *= *h;
	break;
    }

    return size;
}
#endif
#endif
