/*
 * $Workfile: amd_lx_video.c $
 * $Revision: #3 $
 * $Author: raymondd $
 *
 * File Contents: This file consists of main Xfree video supported routines.
 *
 * Project:       Geode Xfree Frame buffer device driver.
 *
 */

/* <LIC_AMD_STD>
 * Copyright (c) 2003-2005 Advanced Micro Devices, Inc.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to 
 * deal in the Software without restriction, including without limitation the 
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 
 * sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *  
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 *  
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
 * IN THE SOFTWARE.
 * 
 * Neither the name of the Advanced Micro Devices, Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 * </LIC_AMD_STD>  */
/* <CTL_AMD_STD>
 * </CTL_AMD_STD>  */
/* <DOC_AMD_STD>
 * </DOC_AMD_STD>  */

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

/* 
 * Fixes & Extensions to support Y800 greyscale modes 
 * Alan Hourihane <alanh@fairlite.demon.co.uk>
 */
#ifndef AMD_V4L2_VIDEO
#include "xf86.h"
#include "xf86_OSproc.h"
#include "xf86Resources.h"
#include "xf86_ansic.h"
#include "compiler.h"
#include "xf86PciInfo.h"
#include "xf86Pci.h"
#include "xf86fbman.h"
#include "regionstr.h"

#include "nsc.h"
#include <X11/extensions/Xv.h>
#include "xaa.h"
#include "xaalocal.h"
#include "dixstruct.h"
#include "fourcc.h"
#include "nsc_fourcc.h"

#if DEBUGLVL>0
#define DBLOG(n,s...) do { if((DEBUGLVL)>=(n)) fprintf(zdfp,s); } while(0)
#include "xf86_ansic.h"
extern FILE *zdfp;
#else
#define DBLOG(n,s...) do {} while(0)
#endif

#define OFF_DELAY 	200		/* milliseconds */
#define FREE_DELAY 	60000

#define OFF_TIMER 	0x01
#define FREE_TIMER	0x02
#define CLIENT_VIDEO_ON	0x04

#define TIMER_MASK      (OFF_TIMER | FREE_TIMER)
#define XV_PROFILE 0
#define REINIT  1

#define DBUF 1
void LXInitVideo(ScreenPtr pScrn);
void LXResetVideo(ScrnInfoPtr pScrni);
static XF86VideoAdaptorPtr LXSetupImageVideo(ScreenPtr);
static void LXInitOffscreenImages(ScreenPtr);
static void LXStopVideo(ScrnInfoPtr, pointer, Bool);
static int LXSetPortAttribute(ScrnInfoPtr, Atom, INT32, pointer);
static int LXGetPortAttribute(ScrnInfoPtr, Atom, INT32 *, pointer);
static void LXQueryBestSize(ScrnInfoPtr, Bool,
			     short, short, short, short, unsigned int *,
			     unsigned int *, pointer);
static int LXPutImage(ScrnInfoPtr, short, short, short, short, short, short,
		       short, short, int, unsigned char *, short, short, Bool,
		       RegionPtr, pointer);
static int LXQueryImageAttributes(ScrnInfoPtr, int, unsigned short *,
				   unsigned short *, int *, int *);

static void LXBlockHandler(int, pointer, pointer, pointer);
void LXSetVideoPosition(int x, int y, int width, int height,
			 short src_w, short src_h, short drw_w,
			 short drw_h, int id, int offset, ScrnInfoPtr pScrni);

extern void LXAccelSync(ScrnInfoPtr pScrni);

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

static Atom xvColorKey, xvColorKeyMode, xvFilter
#if DBUF
  , xvDoubleBuffer
#endif
  ;

/*----------------------------------------------------------------------------
 * LXInitVideo
 *
 * Description	:This is the initialization routine.It creates a new video adapter
 *				 and calls LXSetupImageVideo to initialize the adaptor by filling
 *				 XF86VideoAdaptorREc.Then it lists the existing adaptors and adds the 
 *				 new one to it. Finally the list of XF86VideoAdaptorPtr pointers are
 *				 passed to the xf86XVScreenInit().
 *
 * Parameters.
 * ScreenPtr
 *		pScrn	:Screen handler pointer having screen information.
 *
 * Returns		:none
 *
 * Comments		:none
 *
*----------------------------------------------------------------------------
*/
void
LXInitVideo(ScreenPtr pScrn)
{
   GeodePtr pGeode;
   ScrnInfoPtr pScrni = xf86Screens[pScrn->myNum];
   DBLOG(1,"LXInitVideo()\n");

   pGeode = GEODEPTR(pScrni);

   if (!pGeode->NoAccel) {
      XF86VideoAdaptorPtr *adaptors, *newAdaptors = NULL;
      XF86VideoAdaptorPtr newAdaptor = NULL;

      int num_adaptors;

      newAdaptor = LXSetupImageVideo(pScrn);
      LXInitOffscreenImages(pScrn);

      num_adaptors = xf86XVListGenericAdaptors(pScrni, &adaptors);

      if (newAdaptor) {
	 if (!num_adaptors) {
	    num_adaptors = 1;
	    adaptors = &newAdaptor;
	 } else {
	    newAdaptors =		/* need to free this someplace */
		  xalloc((num_adaptors + 1) * sizeof(XF86VideoAdaptorPtr *));
	    if (newAdaptors) {
	       memcpy(newAdaptors, adaptors, num_adaptors *
		      sizeof(XF86VideoAdaptorPtr));
	       newAdaptors[num_adaptors] = newAdaptor;
	       adaptors = newAdaptors;
	       num_adaptors++;
	    }
	 }
      }

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

      if (newAdaptors)
	 xfree(newAdaptors);
   }
}

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

#define NUM_FORMATS 4

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

#if DBUF
#define NUM_ATTRIBUTES 4
#else
#define NUM_ATTRIBUTES 3
#endif

static XF86AttributeRec Attributes[NUM_ATTRIBUTES] = {
#if DBUF
   {XvSettable | XvGettable, 0, 1, "XV_DOUBLE_BUFFER"},
#endif
   {XvSettable | XvGettable, 0, (1 << 24) - 1, "XV_COLORKEY"},
   {XvSettable | XvGettable, 0, 1, "XV_FILTER"},
   {XvSettable | XvGettable, 0, 1, "XV_COLORKEYMODE"}
};

static XF86ImageRec Images[] = {
   XVIMAGE_UYVY,
   XVIMAGE_YUY2,
   XVIMAGE_Y2YU,
   XVIMAGE_YVYU,
   XVIMAGE_Y800,
   XVIMAGE_I420,
   XVIMAGE_YV12
};

#define NUM_IMAGES (sizeof(Images)/sizeof(Images[0]));

typedef struct
{
   FBAreaPtr area;
   FBLinearPtr linear;
   RegionRec clip;
   CARD32 filter;
   CARD32 colorKey;
   CARD32 colorKeyMode;
   CARD32 videoStatus;
   Time offTime;
   Time freeTime;
#if DBUF
   Bool doubleBuffer;
   int currentBuffer;
#endif
}
GeodePortPrivRec, *GeodePortPrivPtr;

#define GET_PORT_PRIVATE(pScrni) \
   (GeodePortPrivPtr)((GEODEPTR(pScrni))->adaptor->pPortPrivates[0].ptr)

/*----------------------------------------------------------------------------
 * LXSetColorKey
 *
 * Description	:This function reads the color key for the pallete and
 *				  sets the video color key register.
 *
 * Parameters.
 * ScreenInfoPtr
 *		pScrni	:Screen  pointer having screen information.
 *		pPriv	:Video port private data
 *
 * Returns		:none
 *
 * Comments		:none
 *
*----------------------------------------------------------------------------
*/
static INT32
LXSetColorkey(ScrnInfoPtr pScrni, GeodePortPrivPtr pPriv)
{
   int red, green, blue;
   unsigned long key;

   switch (pScrni->depth) {
   case 8:
      vg_get_display_palette_entry(pPriv->colorKey & 0xFF, &key);
      red = ((key >> 16) & 0xFF);
      green = ((key >> 8) & 0xFF);
      blue = (key & 0xFF);
      break;
   case 16:
      red = (pPriv->colorKey & pScrni->mask.red) >>
	    pScrni->offset.red << (8 - pScrni->weight.red);
      green = (pPriv->colorKey & pScrni->mask.green) >>
	    pScrni->offset.green << (8 - pScrni->weight.green);
      blue = (pPriv->colorKey & pScrni->mask.blue) >>
	    pScrni->offset.blue << (8 - pScrni->weight.blue);
      break;
   default:
      /* for > 16 bpp we send in the mask in xf86SetWeight. This
       * function is providing the offset by 1 more. So we take 
       * this as a special case and subtract 1 for > 16
       */
      red = (pPriv->colorKey & pScrni->mask.red) >>
	    (pScrni->offset.red - 1) << (8 - pScrni->weight.red);
      green = (pPriv->colorKey & pScrni->mask.green) >>
	    (pScrni->offset.green - 1) << (8 - pScrni->weight.green);
      blue = (pPriv->colorKey & pScrni->mask.blue) >>
	    (pScrni->offset.blue - 1) << (8 - pScrni->weight.blue);
      break;
   }

   DBLOG(1,"LXSetColorkey() %08x %d\n",blue|(green<<8)|(red<<16),pPriv->colorKeyMode);
   if( pPriv->colorKeyMode != 0 )
      df_set_video_color_key((blue | (green << 8) | (red << 16)), 0xFFFFFF, 1);
   else
      df_set_video_color_key(0,0,1);
   REGION_EMPTY(pScrni->pScreen, &pPriv->clip);
   return 0;
}

/*----------------------------------------------------------------------------
 * LXResetVideo
 *
 * Description	: This function resets the video
 *
 * Parameters.
 * ScreenInfoPtr
 *		pScrni	:Screen  pointer having screen information.
 *
 * Returns		:None
 *
 * Comments		:none
 *
*----------------------------------------------------------------------------
*/

void
LXResetVideo(ScrnInfoPtr pScrni)
{
   GeodePtr pGeode = GEODEPTR(pScrni);
   DBLOG(1,"LXResetVideo()\n");

   if (!pGeode->NoAccel) {
      GeodePortPrivPtr pPriv = pGeode->adaptor->pPortPrivates[0].ptr;

      LXAccelSync(pScrni);
      df_set_video_palette(NULL);
      LXSetColorkey(pScrni, pPriv);
   }
}

/*----------------------------------------------------------------------------
 * LXSetupImageVideo
 *
 * Description	: This function allocates space for a Videoadaptor and initializes
 *				  the XF86VideoAdaptorPtr record.
 *
 * Parameters.
 * ScreenPtr
 *		pScrn	:Screen handler pointer having screen information.
 *
 * Returns		:XF86VideoAdaptorPtr :- pointer to the initialized video adaptor record.
 *
 * Comments		:none
 *
*----------------------------------------------------------------------------
*/

static XF86VideoAdaptorPtr
LXSetupImageVideo(ScreenPtr pScrn)
{
   ScrnInfoPtr pScrni = xf86Screens[pScrn->myNum];
   GeodePtr pGeode = GEODEPTR(pScrni);
   XF86VideoAdaptorPtr adapt;
   GeodePortPrivPtr pPriv;
   DBLOG(1,"LXSetupImageVideo()\n");

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

   adapt->type = XvWindowMask | XvInputMask | XvImageMask;
   adapt->flags = VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT;
   adapt->name = "Advanced Micro Devices";
   adapt->nEncodings = 1;
   adapt->pEncodings = DummyEncoding;
   adapt->nFormats = NUM_FORMATS;
   adapt->pFormats = Formats;
   adapt->nPorts = 1;
   adapt->pPortPrivates = (DevUnion *) (&adapt[1]);
   pPriv = (GeodePortPrivPtr) (&adapt->pPortPrivates[1]);
   adapt->pPortPrivates[0].ptr = (pointer) (pPriv);
   adapt->pAttributes = Attributes;
   adapt->nImages = NUM_IMAGES;
   adapt->nAttributes = NUM_ATTRIBUTES;
   adapt->pImages = Images;
   adapt->PutVideo = NULL;
   adapt->PutStill = NULL;
   adapt->GetVideo = NULL;
   adapt->GetStill = NULL;
   adapt->StopVideo = LXStopVideo;
   adapt->SetPortAttribute = LXSetPortAttribute;
   adapt->GetPortAttribute = LXGetPortAttribute;
   adapt->QueryBestSize = LXQueryBestSize;
   adapt->PutImage = LXPutImage;
   adapt->QueryImageAttributes = LXQueryImageAttributes;

   pPriv->filter = 0;
   pPriv->colorKey = pGeode->videoKey;
   pPriv->colorKeyMode = TRUE;
   pPriv->videoStatus = 0;
#if DBUF
   pPriv->doubleBuffer = TRUE;
   pPriv->currentBuffer = 0; /* init to first buffer */
#endif

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

   pGeode->adaptor = adapt;

   pGeode->BlockHandler = pScrn->BlockHandler;
   pScrn->BlockHandler = LXBlockHandler;

   xvColorKey = MAKE_ATOM("XV_COLORKEY");
   xvColorKeyMode = MAKE_ATOM("XV_COLORKEYMODE");
   xvFilter = MAKE_ATOM("XV_FILTER");
#if DBUF
   xvDoubleBuffer = MAKE_ATOM("XV_DOUBLE_BUFFER");
#endif

   LXResetVideo(pScrni);

   return adapt;
}

/*----------------------------------------------------------------------------
 * LXStopVideo
 *
 * Description	:This function is used to stop input and output video
 *
 * Parameters.
 *		pScrni		:Screen handler pointer having screen information.
 *		data		:Pointer to the video port's private data
 *		exit		:Flag indicating whether the offscreen areas used for video
 *					 to be deallocated or not.
 * Returns		:none
 *
 * Comments		:none
 *
*----------------------------------------------------------------------------
*/
static void
LXStopVideo(ScrnInfoPtr pScrni, pointer data, Bool exit)
{
   GeodePortPrivPtr pPriv = (GeodePortPrivPtr) data;
   GeodePtr pGeode = GEODEPTR(pScrni);
   DBLOG(1,"LXStopVideo()\n");

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

   LXAccelSync(pScrni);
   if (exit) {
      if (pPriv->videoStatus & CLIENT_VIDEO_ON) {
	 df_set_video_enable(0,0);
      }
      if (pPriv->area) {
	 xf86FreeOffscreenArea(pPriv->area);
	 pPriv->area = NULL;
      }
      pPriv->videoStatus = 0;
      pGeode->OverlayON = FALSE;
   } else {
      if (pPriv->videoStatus & CLIENT_VIDEO_ON) {
	 pPriv->videoStatus |= OFF_TIMER;
	 pPriv->offTime = currentTime.milliseconds + OFF_DELAY;
      }
   }
}

/*----------------------------------------------------------------------------
 * LXSetPortAttribute
 *
 * Description	:This function is used to set the attributes of a port like colorkeymode,
 *				  double buffer support and filter.
 *
 * Parameters.
 *		pScrni		:Screen handler pointer having screen information.
 *		data		:Pointer to the video port's private data
 *		attribute	:The port attribute to be set
 *		value		:Value of the attribute to be set.  
 *					 
 * Returns		:Sucess if the attribute is supported, else BadMatch
 *
 * Comments		:none
 *
*----------------------------------------------------------------------------
*/
static int
LXSetPortAttribute(ScrnInfoPtr pScrni,
		    Atom attribute, INT32 value, pointer data)
{
   GeodePortPrivPtr pPriv = (GeodePortPrivPtr) data;
   DBLOG(1,"LXSetPortAttribute(%d,%#x)\n",attribute,value);

   LXAccelSync(pScrni);
   if (attribute == xvColorKey) {
      pPriv->colorKey = value;
      LXSetColorkey(pScrni, pPriv);
   }
#if DBUF
   else if (attribute == xvDoubleBuffer) {
      if ((value < 0) || (value > 1))
	 return BadValue;
      pPriv->doubleBuffer = value;
   }
#endif
   else if (attribute == xvColorKeyMode) {
      pPriv->colorKeyMode = value;
      LXSetColorkey(pScrni, pPriv);
   }
   else if (attribute == xvFilter) {
      if ((value < 0) || (value > 1))
	 return BadValue;
      pPriv->filter = value;
   }
   else
      return BadMatch;

   return Success;
}

/*----------------------------------------------------------------------------
 * LXGetPortAttribute
 *
 * Description	:This function is used to get the attributes of a port like hue,
 *				 saturation,brightness or contrast.
 *
 * Parameters.
 *		pScrni		:Screen handler pointer having screen information.
 *		data		:Pointer to the video port's private data
 *		attribute	:The port attribute to be read
 *		value		:Pointer to the value of the attribute to be read.  
 *					 
 * Returns		:Sucess if the attribute is supported, else BadMatch
 *
 * Comments		:none
 *
*----------------------------------------------------------------------------
*/
static int
LXGetPortAttribute(ScrnInfoPtr pScrni,
		    Atom attribute, INT32 * value, pointer data)
{
   GeodePortPrivPtr pPriv = (GeodePortPrivPtr) data;
   DBLOG(1,"LXGetPortAttribute(%d)\n",attribute);

   if (attribute == xvColorKey) {
      *value = pPriv->colorKey;
   }
#if DBUF
   else if (attribute == xvDoubleBuffer) {
      *value = (pPriv->doubleBuffer) ? 1 : 0;
   }
#endif
   else if (attribute == xvColorKeyMode) {
      *value = pPriv->colorKeyMode;
   }
   else if (attribute == xvFilter) {
      *value = pPriv->filter;
   } else
      return BadMatch;

   return Success;
}

/*----------------------------------------------------------------------------
 * LXQueryBestSize
 *
 * Description	:This function provides a way to query what the destination dimensions
 *				 would end up being if they were to request that an area vid_w by vid_h
 *               from the video stream be scaled to rectangle of drw_w by drw_h on 
 *				 the screen.
 *
 * Parameters.
 * ScreenInfoPtr
 *		pScrni		:Screen handler pointer having screen information.
 *		data		:Pointer to the video port's private data
 *      vid_w,vid_h	:Width and height of the video data.
 *		drw_w,drw_h :Width and height of the scaled rectangle.
 *		p_w,p_h		:Width and height of the destination rectangle. 
 *					 
 * Returns		:None
 *
 * Comments		:None
 *
*----------------------------------------------------------------------------
*/
static void
LXQueryBestSize(ScrnInfoPtr pScrni,
		 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;

   if (*p_w > 16384)
      *p_w = 16384;
   DBLOG(1,"LXQueryBestSize(%d, src %dx%d scl %dx%d dst %dx%d)\n",motion,vid_w,vid_h,drw_w,drw_h,*p_w,*p_h);
}

static void
LXCopyGreyscale(unsigned char *src, unsigned char *dst, int srcp, int dstp, int h, int w)
{
   int i;
   unsigned char *src2 = src;
   unsigned char *dst2 = dst;
   unsigned char *dst3;
   unsigned char *src3;

   dstp <<= 1;

   while (h--) {
      dst3 = dst2;
      src3 = src2;
      for (i = 0; i < w; i++) {
	 *dst3++ = *src3++;		/* Copy Y data */
	 *dst3++ = 0x80;		/* Fill UV with 0x80 - greyscale */
      }
      src3 = src2;
      for (i = 0; i < w; i++) {
	 *dst3++ = *src3++;		/* Copy Y data */
	 *dst3++ = 0x80;		/* Fill UV with 0x80 - greyscale */
      }
      dst2 += dstp;
      src2 += srcp;
   }
}

/*----------------------------------------------------------------------------
 * LXCopyData420
 *
 * Description	: Copies data from src to destination
 *
 * Parameters.
 *		src	: pointer to the source data
 *		dst	: pointer to destination data
 *		srcp	: pitch of the srcdata
 *		dstp	: pitch of the destination data 
 *		h & w	: height and width of source data
 *					 
 * Returns		:None
 *
 * Comments		:None
 *
*----------------------------------------------------------------------------
*/

static void
LXCopyData420(unsigned char *src, unsigned char *dst, int srcp, int dstp, int h, int w)
{
   while (h--) {
      memcpy(dst, src, w);
      src += srcp; dst += dstp;
   }
}

/*----------------------------------------------------------------------------
 * LXCopyData422
 *
 * Description	: Copies data from src to destination
 *
 * Parameters.
 *		src			: pointer to the source data
 *		dst			: pointer to destination data
 *		srcp	: pitch of the srcdata
 *		dstp	: pitch of the destination data 
 *		h & w		: height and width of source data
 *					 
 * Returns		:None
 *
 * Comments		:None
 *
*----------------------------------------------------------------------------
*/

static void
LXCopyData422(unsigned char *src, unsigned char *dst,
	       int srcp, int dstp, int h, int w)
{
   w <<= 1;
   while (h--) {
      memcpy(dst, src, w);
      src += srcp; dst += dstp;
   }
}

static FBAreaPtr
LXAllocateMemory(ScrnInfoPtr pScrni, FBAreaPtr area, int numlines)
{
   ScreenPtr pScrn = screenInfo.screens[pScrni->scrnIndex];
   FBAreaPtr new_area;

   if (area) {
      if ((area->box.y2 - area->box.y1) >= numlines)
	 return area;

      if (xf86ResizeOffscreenArea(area, pScrni->displayWidth, numlines))
	 return area;

      xf86FreeOffscreenArea(area);
   }

   new_area = xf86AllocateOffscreenArea(pScrn, pScrni->displayWidth,
					numlines, 0, NULL, NULL, NULL);

   if (!new_area) {
      int max_w, max_h;

      xf86QueryLargestOffscreenArea(pScrn, &max_w, &max_h, 0,
				    FAVOR_WIDTH_THEN_AREA, PRIORITY_EXTREME);

      if ((max_w < pScrni->displayWidth) || (max_h < numlines))
	 return NULL;

      xf86PurgeUnlockedOffscreenAreas(pScrn);
      new_area = xf86AllocateOffscreenArea(pScrn, pScrni->displayWidth,
					   numlines, 0, NULL, NULL, NULL);
   }

   return new_area;
}

static BoxRec dstBox;
static int srcPitch = 0, srcPitch2 = 0, dstPitch = 0, dstPitch2 = 0;
static INT32 Bx1, Bx2, By1, By2;
static int top, left, npixels, nlines;
static int offset, s1offset = 0, s2offset = 0, s3offset = 0;
static unsigned char *dst_start;
static int d2offset = 0, d3offset = 0;

static DF_VIDEO_SOURCE_PARAMS vSrcParams;

void
LXSetVideoPosition(int x, int y, int width, int height,
		    short src_w, short src_h, short drw_w, short drw_h,
		    int id, int offset, ScrnInfoPtr pScrni)
{
   long ystart, xend, yend;
   unsigned long lines = 0;
   unsigned long y_extra, uv_extra = 0;
   DF_VIDEO_POSITION vidPos;

   DBLOG(1,"LXSetVideoPosition(%d,%d %dx%d, src %dx%d, dst %dx%d, id %d, ofs %d)\n",
           x,y,width,height,src_w,src_h,drw_w,drw_h,id,offset);

   xend = x + drw_w;
   yend = y + drw_h;

   /*  TOP CLIPPING */

   if (y < 0) {
      if (src_h < drw_h)
	 lines = (-y) * src_h / drw_h;
      else
	 lines = (-y);
      ystart = 0;
      drw_h += y;
      y_extra = lines * dstPitch;
      uv_extra = (lines >> 1) * (dstPitch2);
   } else {
      ystart = y;
      lines = 0;
      y_extra = 0;
   }

   memset(&vidPos,0,sizeof(vidPos));
   vidPos.x= x;
   vidPos.y = ystart;
   vidPos.width = xend - x;
   vidPos.height = yend - ystart;

   DBLOG(1,"video_pos %d,%d %dx%d\n",vidPos.x,vidPos.y,vidPos.width,vidPos.height);
   df_set_video_position(&vidPos);

   vSrcParams.y_offset = offset + y_extra;
   if ((id == FOURCC_Y800) || (id == FOURCC_I420) || (id == FOURCC_YV12)) {
      vSrcParams.u_offset = offset + d3offset + uv_extra;
      vSrcParams.v_offset = offset + d2offset + uv_extra;
   }
   else {
      vSrcParams.u_offset = vSrcParams.v_offset = 0;
   }
   vSrcParams.flags = DF_SOURCEFLAG_IMPLICITSCALING;

   DBLOG(1,"video_format %#x yofs %#x uofs %#x vofs %#x yp %d uvp %d wh %dx%d flg %#x\n",
        vSrcParams.video_format, vSrcParams.y_offset, vSrcParams.u_offset,
        vSrcParams.v_offset, vSrcParams.y_pitch, vSrcParams.uv_pitch,
        vSrcParams.width, vSrcParams.height, vSrcParams.flags);
 
   df_configure_video_source(&vSrcParams, &vSrcParams); 
}

/*----------------------------------------------------------------------------
 * LXDisplayVideo
 *
 * Description	: This function sets up the video registers for playing video
 *		  It sets up the video format,width, height & position of the 
 *		  video window ,video offsets( y,u,v) and video pitches(y,u,v)	
 * Parameters.
 *					 
 * Returns	:None
 *
 * Comments	:None
 *
*----------------------------------------------------------------------------
*/

static void
LXDisplayVideo(ScrnInfoPtr pScrni,
		int id,
		int offset,
		short width, short height,
		int pitch,
		int x1, int y1, int x2, int y2,
		BoxPtr dstBox,
		short src_w, short src_h, short drw_w, short drw_h)
{
   DBLOG(1,"LXDisplayVideo(id %d, ofs %d, %dx%d, p %d, %d,%d, %d,%d, src %dx%d dst %dx%d)\n",
         id,offset,width,height,pitch,x1,y1,x2,y2,src_w,src_h,drw_w,drw_h);

   LXAccelSync(pScrni);

   switch (id) {
   case FOURCC_UYVY: vSrcParams.video_format = DF_VIDFMT_UYVY; break;
   case FOURCC_Y800:
   case FOURCC_YV12:
   case FOURCC_I420: vSrcParams.video_format = DF_VIDFMT_Y0Y1Y2Y3; break;
   case FOURCC_YUY2: vSrcParams.video_format = DF_VIDFMT_YUYV; break;
   case FOURCC_Y2YU: vSrcParams.video_format = DF_VIDFMT_Y2YU; break;
   case FOURCC_YVYU: vSrcParams.video_format = DF_VIDFMT_YVYU; break;
   }

   vSrcParams.width = width;
   vSrcParams.height = height;
   vSrcParams.y_pitch = dstPitch;
   vSrcParams.uv_pitch = dstPitch2;

   df_set_video_filter_coefficients(NULL,1);
   if ((drw_w >= src_w) && (drw_h >= src_h))
      df_set_video_scale(width, height, drw_w, drw_h, 
         DF_SCALEFLAG_CHANGEX | DF_SCALEFLAG_CHANGEY);
   else if (drw_w < src_w)
      df_set_video_scale(drw_w, height, drw_w, drw_h,
         DF_SCALEFLAG_CHANGEX | DF_SCALEFLAG_CHANGEY);
   else if (drw_h < src_h)
      df_set_video_scale(width, drw_h, drw_w, drw_h,
         DF_SCALEFLAG_CHANGEX | DF_SCALEFLAG_CHANGEY);

   LXSetVideoPosition(dstBox->x1, dstBox->y1, width, height, src_w,
		       src_h, drw_w, drw_h, id, offset, pScrni);

   df_set_video_enable(1,0);
}

#if REINIT
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

/*----------------------------------------------------------------------------
 * LXPutImage	: This function writes a single frame of video into a drawable.
 *		The position and size of the source rectangle is specified by src_x,src_y,
 *		src_w and src_h. This data is stored in a system memory buffer at buf.  
 *		The position and size of the destination rectangle is specified by drw_x,
 *      drw_y,drw_w,drw_h.The data is in the format indicated by the image descriptor 
 *		and represents a source of size width by height.  If sync is TRUE the driver 
 *		should not return from this function until it is through reading the data from 
 *		buf.  Returning when sync is TRUE indicates that it is safe for the data at buf
 *		to be replaced,freed, or modified.
 *
 *
 * Description		: 
 * Parameters.
 *					 
 * Returns		:None
 *
 * Comments		:None
 *
*----------------------------------------------------------------------------
*/

static int
LXPutImage(ScrnInfoPtr pScrni,
	    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)
{
   GeodePortPrivPtr pPriv = (GeodePortPrivPtr) data;
   GeodePtr pGeode = GEODEPTR(pScrni);
   int new_h;

#if REINIT
   BOOL ReInitVideo = FALSE;
   static BOOL DoReinitAgain = 0;
#endif

#if XV_PROFILE
   long oldtime, newtime;

   UpdateCurrentTime();
   oldtime = currentTime.milliseconds;
#endif
   DBLOG(1,"LXPutImage(src %d,%d %dx%d dst %d,%d %dx%d, id %d %dx%d sync %d)\n",
     src_x,src_y,src_w,src_h,drw_x,drw_y,drw_w,drw_h,id,width,height,sync);

#if REINIT
/* update cliplist */
   if (!RegionsEqual(&pPriv->clip, clipBoxes)) {
      ReInitVideo = TRUE;
   }
   if (DoReinitAgain)
      ReInitVideo = TRUE;

   if (ReInitVideo) {
      DBLOG(1, "Regional Not Equal - Init\n");
#endif
      DoReinitAgain = ~DoReinitAgain;
      if (drw_w > 16384)
	 drw_w = 16384;

      /* Clip */
      Bx1 = src_x;
      Bx2 = src_x + src_w;
      By1 = src_y;
      By2 = src_y + src_h;

      if ((Bx1 >= Bx2) || (By1 >= By2))
	 return Success;

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

      dstBox.x1 -= pScrni->frameX0;
      dstBox.x2 -= pScrni->frameX0;
      dstBox.y1 -= pScrni->frameY0;
      dstBox.y2 -= pScrni->frameY0;

      switch (id) {
      case FOURCC_YV12:
      case FOURCC_I420:

      srcPitch = (width + 3) & ~3; /* of luma */
      dstPitch = (width + 31) & ~31;

      s2offset = srcPitch * height;
      d2offset = dstPitch * height;

      srcPitch2 = ((width >> 1) + 3) & ~3;
      dstPitch2 = ((width >> 1) + 15) & ~15;

      s3offset = (srcPitch2 * (height >> 1)) + s2offset;
      d3offset = (dstPitch2 * (height >> 1)) + d2offset;

      new_h = dstPitch * height;	/* Y */
      new_h += (dstPitch2 * height);	/* U+V */
      new_h += pGeode->Pitch - 1;
      new_h /= pGeode->Pitch;
      break;

      case FOURCC_UYVY:
      case FOURCC_YUY2:
      case FOURCC_Y800:
      default:
      dstPitch = ((width << 1) + 3) & ~3;
      srcPitch = (width << 1);
      new_h = ((dstPitch * height) + pGeode->Pitch - 1) / pGeode->Pitch;
      break;
      }

#if DBUF
      if (pPriv->doubleBuffer)
         new_h <<= 1;
#endif

      if (!(pPriv->area = LXAllocateMemory(pScrni, pPriv->area, new_h)))
         return BadAlloc;

      /* copy data */
      top = By1;
      left = Bx1 & ~1;
      npixels = ((Bx2 + 1) & ~1) - left;

      switch (id) {
      case FOURCC_YV12:
      case FOURCC_I420:
	 {
	    int tmp;

	    top &= ~1;
	    offset = (pPriv->area->box.y1 * pGeode->Pitch) + (top * dstPitch);

#if DBUF
	    if (pPriv->doubleBuffer && pPriv->currentBuffer)
	       offset += (new_h >> 1) * pGeode->Pitch;
#endif

	    dst_start = pGeode->FBBase + offset + left;
	    tmp = ((top >> 1) * srcPitch2) + (left >> 1);
	    s2offset += tmp;
	    s3offset += tmp;
	    if (id == FOURCC_I420) {
	       tmp = s2offset;
	       s2offset = s3offset;
	       s3offset = tmp;
	    }
	    nlines = ((By2 + 1) & ~1) - top;
	 }
	 break;
      case FOURCC_UYVY:
      case FOURCC_YUY2:
      case FOURCC_Y800:
      default:
	 left <<= 1;
	 buf += (top * srcPitch) + left;
	 nlines = By2 - top;
	 offset = (pPriv->area->box.y1 * pGeode->Pitch) + (top * dstPitch);
#if DBUF
	 if (pPriv->doubleBuffer && pPriv->currentBuffer)
	    offset += (new_h >> 1) * pGeode->Pitch;
#endif

	 dst_start = pGeode->FBBase + offset + left;
	 break;
      }
      s1offset = (top * srcPitch) + left;

#if REINIT
      /* update cliplist */
      REGION_COPY(pScrni->pScreen, &pPriv->clip, clipBoxes);
      if (pPriv->colorKeyMode == 0) {
	 /* draw these */
	 xf86XVFillKeyHelper(pScrni->pScreen, pPriv->colorKey, clipBoxes);
      }
      LXDisplayVideo(pScrni, id, offset, width, height, dstPitch,
		      Bx1, By1, Bx2, By2, &dstBox, src_w, src_h, drw_w,
		      drw_h);
   }
#endif

   switch (id) {

   case FOURCC_Y800:
      LXCopyGreyscale(buf, dst_start, srcPitch, dstPitch, nlines, npixels);
      break;
   case FOURCC_YV12:
   case FOURCC_I420:
      LXCopyData420(buf + s1offset, dst_start, srcPitch, dstPitch, nlines,
		     npixels);
      LXCopyData420(buf + s2offset, dst_start + d2offset, srcPitch2,
		     dstPitch2, nlines >> 1, npixels >> 1);
      LXCopyData420(buf + s3offset, dst_start + d3offset, srcPitch2,
		     dstPitch2, nlines >> 1, npixels >> 1);
      break;
   case FOURCC_UYVY:
   case FOURCC_YUY2:
   default:
      LXCopyData422(buf, dst_start, srcPitch, dstPitch, nlines, npixels);
      break;
   }
#if !REINIT
   /* update cliplist */
   REGION_COPY(pScrni->pScreen, &pPriv->clip, clipBoxes);
   if (pPriv->colorKeyMode == 0) {
      /* draw these */
      XAAFillSolidRects(pScrni, pPriv->colorKey, GXcopy, ~0,
			REGION_NUM_RECTS(clipBoxes), REGION_RECTS(clipBoxes));
   }
   LXDisplayVideo(pScrni, id, offset, width, height, dstPitch,
		   Bx1, By1, Bx2, By2, &dstBox, src_w, src_h, drw_w, drw_h);
#endif

#if XV_PROFILE
   UpdateCurrentTime();
   newtime = currentTime.milliseconds;
   DBLOG(1, "PI %d\n", newtime - oldtime);
#endif

#if DBUF
   pPriv->currentBuffer ^= 1;
#endif

   pPriv->videoStatus = CLIENT_VIDEO_ON;
   pGeode->OverlayON = TRUE;
   return Success;
}

/*----------------------------------------------------------------------------
 * LXQueryImageAttributes
 *
 * Description	:This function is called to let the driver specify how data
 *				 for a particular image of size width by height should be 
 *				 stored. 		
 *
 * Parameters.
 *		pScrni		:Screen handler pointer having screen information.
 *		id		:Id for the video format
 *		width		:width  of the image (can be modified by the driver)  
 *		height		:height of the image (can be modified by the driver)  
 * Returns		: Size of the memory required for storing this image
 *
 * Comments		:None
 *
*----------------------------------------------------------------------------
*/
static int
LXQueryImageAttributes(ScrnInfoPtr pScrni,
			int id,
			unsigned short *w, unsigned short *h,
			int *pitches, int *offsets)
{
   int size;
   int tmp;

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

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

   switch (id) {
   case FOURCC_YV12:
   case FOURCC_I420:
      *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 FOURCC_UYVY:
   case FOURCC_YUY2:
   case FOURCC_Y800:
   default:
      size = *w << 1;
      if (pitches)
	 pitches[0] = size;
      size *= *h;
      break;
   }
   DBLOG(1,"LXQueryImageAttributes(%d)= %d, %dx%d %d/%d/%d %d/%d/%d\n",
      id,size,*w,*h,pitches[0],pitches[1],pitches[2],offsets[0],offsets[1],offsets[2]);
   return size;
}

static void
LXBlockHandler(int i, pointer blockData, pointer pTimeout, pointer pReadmask)
{
   ScreenPtr pScrn = screenInfo.screens[i];
   ScrnInfoPtr pScrni = xf86Screens[i];
   GeodePtr pGeode = GEODEPTR(pScrni);
   GeodePortPrivPtr pPriv = GET_PORT_PRIVATE(pScrni);

   pScrn->BlockHandler = pGeode->BlockHandler;
   (*pScrn->BlockHandler) (i, blockData, pTimeout, pReadmask);
   pScrn->BlockHandler = LXBlockHandler;

   if (pPriv->videoStatus & TIMER_MASK) {
      DBLOG(1,"LXBlockHandler(%d)\n",i);
      LXAccelSync(pScrni);
      UpdateCurrentTime();
      if (pPriv->videoStatus & OFF_TIMER) {
	 if (pPriv->offTime < currentTime.milliseconds) {
	    df_set_video_enable(0,0);
	    pPriv->videoStatus = FREE_TIMER;
	    pPriv->freeTime = currentTime.milliseconds + FREE_DELAY;
	 }
      } else {				/* FREE_TIMER */
	 if (pPriv->freeTime < currentTime.milliseconds) {
	    if (pPriv->area) {
	       xf86FreeOffscreenArea(pPriv->area);
	       pPriv->area = NULL;
	    }
	    pPriv->videoStatus = 0;
	 }
      }
   }
}

/****************** Offscreen stuff ***************/

typedef struct
{
   FBAreaPtr area;
   FBLinearPtr linear;
   Bool isOn;
}
OffscreenPrivRec, *OffscreenPrivPtr;

/*----------------------------------------------------------------------------
 * LXAllocateSurface
 *
 * Description	:This function allocates an area of w by h in the offscreen
 * Parameters.
 *		pScrni	:Screen handler pointer having screen information.
 * 
 * Returns		:None
 *
 * Comments		:None
 *
*----------------------------------------------------------------------------
*/

static int
LXAllocateSurface(ScrnInfoPtr pScrni,
		   int id,
		   unsigned short w, unsigned short h, XF86SurfacePtr surface)
{
   FBAreaPtr area;
   int pitch, fbpitch, numlines;
   OffscreenPrivPtr pPriv;
   DBLOG(1,"LXAllocateSurface(id %d, %dx%d)\n",id,w,h);

   if ((w > 1024) || (h > 1024))
      return BadAlloc;

   w = (w + 1) & ~1;
   pitch = ((w << 1) + 15) & ~15;
   fbpitch = pScrni->bitsPerPixel * pScrni->displayWidth >> 3;
   numlines = ((pitch * h) + fbpitch - 1) / fbpitch;

   if (!(area = LXAllocateMemory(pScrni, NULL, numlines)))
      return BadAlloc;

   surface->width = w;
   surface->height = h;

   if (!(surface->pitches = xalloc(sizeof(int))))
      return BadAlloc;
   if (!(surface->offsets = xalloc(sizeof(int)))) {
      xfree(surface->pitches);
      return BadAlloc;
   }
   if (!(pPriv = xalloc(sizeof(OffscreenPrivRec)))) {
      xfree(surface->pitches);
      xfree(surface->offsets);
      return BadAlloc;
   }

   pPriv->area = area;
   pPriv->isOn = FALSE;

   surface->pScrn = pScrni;
   surface->id = id;
   surface->pitches[0] = pitch;
   surface->offsets[0] = area->box.y1 * fbpitch;
   surface->devPrivate.ptr = (pointer) pPriv;

   return Success;
}

static int
LXStopSurface(XF86SurfacePtr surface)
{
   OffscreenPrivPtr pPriv = (OffscreenPrivPtr) surface->devPrivate.ptr;
   DBLOG(1,"LXStopSurface()\n");

   if (pPriv->isOn) {
      pPriv->isOn = FALSE;
   }

   return Success;
}

static int
LXFreeSurface(XF86SurfacePtr surface)
{
   OffscreenPrivPtr pPriv = (OffscreenPrivPtr) surface->devPrivate.ptr;
   DBLOG(1,"LXFreeSurface()\n");

   if (pPriv->isOn)
      LXStopSurface(surface);
   xf86FreeOffscreenArea(pPriv->area);
   xfree(surface->pitches);
   xfree(surface->offsets);
   xfree(surface->devPrivate.ptr);

   return Success;
}

static int
LXGetSurfaceAttribute(ScrnInfoPtr pScrni, Atom attribute, INT32 * value)
{
   return LXGetPortAttribute(pScrni, attribute, value,
			      (pointer) (GET_PORT_PRIVATE(pScrni)));
}

static int
LXSetSurfaceAttribute(ScrnInfoPtr pScrni, Atom attribute, INT32 value)
{
   return LXSetPortAttribute(pScrni, attribute, value,
			      (pointer) (GET_PORT_PRIVATE(pScrni)));
}

static int
LXDisplaySurface(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)
{
   OffscreenPrivPtr pPriv = (OffscreenPrivPtr) surface->devPrivate.ptr;
   ScrnInfoPtr pScrni = surface->pScrn;
   GeodePortPrivPtr portPriv = GET_PORT_PRIVATE(pScrni);
   INT32 x1, y1, x2, y2;
   BoxRec dstBox;
   DBLOG(1,"LXDisplaySurface(src %d,%d %dx%d, dst %d,%d %dx%d)\n",
         src_x,src_y,src_w,src_h,drw_x,drw_y,drw_w,drw_h);

   DEBUGMSG(0, (0, X_NONE, "DisplaySuface\n"));
   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;

   if ((x1 >= x2) || (y1 >= y2))
      return Success;

   dstBox.x1 -= pScrni->frameX0;
   dstBox.x2 -= pScrni->frameX0;
   dstBox.y1 -= pScrni->frameY0;
   dstBox.y2 -= pScrni->frameY0;

   xf86XVFillKeyHelper(pScrni->pScreen, portPriv->colorKey, clipBoxes);

   LXDisplayVideo(pScrni, surface->id, surface->offsets[0],
		   surface->width, surface->height, surface->pitches[0],
		   x1, y1, x2, y2, &dstBox, src_w, src_h, drw_w, drw_h);

   pPriv->isOn = TRUE;
   if (portPriv->videoStatus & CLIENT_VIDEO_ON) {
      REGION_EMPTY(pScrni->pScreen, &portPriv->clip);
      UpdateCurrentTime();
      portPriv->videoStatus = FREE_TIMER;
      portPriv->freeTime = currentTime.milliseconds + FREE_DELAY;
   }

   return Success;
}

/*----------------------------------------------------------------------------
 * LXInitOffscreenImages
 *
 * Description	:This function sets up the offscreen memory management.It fills 
 *				 in the XF86OffscreenImagePtr structure with functions to handle
 *				 offscreen memory operations. 	
 *
 * Parameters.
 *		pScrn	:Screen handler pointer having screen information.
 * 
 * Returns		: None
 *
 * Comments		:None
 *
*----------------------------------------------------------------------------
*/
static void
LXInitOffscreenImages(ScreenPtr pScrn)
{
   XF86OffscreenImagePtr offscreenImages;
   DBLOG(1,"LXInitOffscreenImages()\n");

   /* need to free this someplace */
   if (!(offscreenImages = xalloc(sizeof(XF86OffscreenImageRec))))
      return;

   offscreenImages[0].image = &Images[0];
   offscreenImages[0].flags = VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT;
   offscreenImages[0].alloc_surface = LXAllocateSurface;
   offscreenImages[0].free_surface = LXFreeSurface;
   offscreenImages[0].display = LXDisplaySurface;
   offscreenImages[0].stop = LXStopSurface;
   offscreenImages[0].setAttribute = LXSetSurfaceAttribute;
   offscreenImages[0].getAttribute = LXGetSurfaceAttribute;
   offscreenImages[0].max_width = 1024;
   offscreenImages[0].max_height = 1024;
   offscreenImages[0].num_attributes = NUM_ATTRIBUTES;
   offscreenImages[0].attributes = Attributes;

   xf86XVRegisterOffscreenImages(pScrn, offscreenImages, 1);
}

#endif /* !AMD_V4L2_VIDEO */

