/* *
 * XVideo support based on PXA27x overlay
 *  and using PXA3xx-GFX for scaling.
 *
 * This is mostly just forcing together vo_pxa.c 
 *  from mplayer and ati_video.c from kdrive.
 *
 * Maybe later even using the PXA3xx hardware video acceleration.
 *
 * Formats that we support, or might support:
 * I420		Planar - 8bit Y, 8bit 2x2 subsampled U then V
 * IYUV/YV12	Planar - 8bit Y, 8bit 2x2 subsampled V then U
 * UYVY		Packed - [U01 Y0 V01 Y1] for each 2 pixels horizontally all 8bit
 * YUY2		Packed - [Y0 U01 Y1 V01] for each 2 pixels horizontally all 8bit
 */



#include    "kaa.h"

#include    "gcstruct.h"
#include    "scrnintstr.h"
#include    "pixmapstr.h"
#include    "regionstr.h"
#include    "mistruct.h"
#include    "dixfontstr.h"
#include    "fb.h"
#include    "migc.h"
#include    "miline.h"
#include <sys/ioctl.h>
#include "kxv.h"

#ifdef HAVE_CONFIG_H
#include <kdrive-config.h>
#endif

#include "pxa3xx-accel.h"
#include "pxa3xx-video.h"
#include "fbdev.h"

#define OP_VIDEO	6

#define DEBUG

#ifdef DEBUG
#define dbg(format, arg...)		\
	fprintf(stderr, format , ## arg);fflush(stderr)
#else
#define dbg(format, arg...){ }
#endif

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


//FIXME: Set these from somewhere?
#define IMAGE_MAX_WIDTH		2048
#define IMAGE_MAX_HEIGHT	2048

struct pxa3xxVideoPriv *globalVPriv = NULL;  /* FIXME: Make dynamic */

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

#define NUM_FORMATS 3
static KdVideoFormatRec Formats[NUM_FORMATS] = 
{
	{15, TrueColor}, {16, TrueColor}, {24, TrueColor}
};

#define NUM_ATTRIBUTES 0
static KdAttributeRec Attributes[NUM_ATTRIBUTES] =
{
};

#define NUM_IMAGES 4
static KdImageRec Images[NUM_IMAGES] =
{
	XVIMAGE_YV12,
	XVIMAGE_I420,
//	XVIMAGE_YUY2,
//	XVIMAGE_UYVY
};

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

//FIXME: why are these static and out of the priv structure?
static Atom xvBrightness, xvSaturation;


void PXA3xxStopVideo(KdScreenInfo *screen, pointer data, Bool exit){ 
	struct pxa3xxVideoPriv *vidPriv = globalVPriv;

	dbg("VID: +-PXA3xxStopVideo()\n");

	/* The GFX might still be working so make sure everything is done.
	 * TODO: do something with failure here. Will need to fallback. */
	pxa3xxGFX_Wait_Complete();

	//kill the overlay
	//setupOverlay(&vidPriv->ov[1], 0, 0, 0, 0, 0, 0);
	closeOverlay(&vidPriv->ov[1]);

	ScreenPtr pScreen = screen->pScreen;
	PXA3xxPortPrivPtr pPortPriv = (PXA3xxPortPrivPtr)data;

	REGION_EMPTY(screen->pScreen, &pPortPriv->clip);   

	if (pPortPriv->off_screen) {
		KdOffscreenFree (pScreen, pPortPriv->off_screen);
		pPortPriv->off_screen = 0;
	}
}

int PXA3xxSetPortAttribute(KdScreenInfo *screen, Atom attribute, int value, pointer data) {
	dbg("VID: +-PXA3xxSetPortAttribute()\n");
	return BadMatch;
}

int PXA3xxGetPortAttribute(KdScreenInfo *screen, Atom attribute, int *value, pointer data) {
	dbg("VID: +-PXA3xxGetPortAttribute()\n");
	return BadMatch;
}

void PXA3xxQueryBestSize(KdScreenInfo *screen, Bool motion, short vid_w, short vid_h, 
		short drw_w, short drw_h, unsigned int *p_w, unsigned int *p_h, pointer data) {
	dbg("VID: +-PXA3xxQueryBestSize()\n");
	*p_w = drw_w;
	*p_h = drw_h;
}

void PXA3xxVideoSave(ScreenPtr pScreen, KdOffscreenArea *area) {
	struct pxa3xxVideoPriv *vidPriv = globalVPriv;

	PXA3xxPortPrivPtr pPortPriv = vidPriv->pAdaptor->pPortPrivates[0].ptr;

	if (pPortPriv->off_screen == area)
		pPortPriv->off_screen = 0;
}

/** Copy and display the image */
int PXA3xxPutImage(KdScreenInfo *screen, DrawablePtr pDraw,
		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) {	
	struct pxa3xxVideoPriv *vidPriv = globalVPriv;
	PXA3xxPortPrivPtr pPortPriv = (PXA3xxPortPrivPtr)data;
	ScreenPtr pScreen = screen->pScreen;
	KdScreenPriv(pScreen);

	INT32 x1, x2, y1, y2;
	int srcPitch;
	int size;
	BoxRec dstBox;

	/* 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;

//	dbg("dstBox: (%i,%i)-(%i,%i)\n", dstBox.x1, dstBox.y1, dstBox.x2, dstBox.y2);
//	dbg("src: (%i,%i)-(%i,%i) (%ix%i)\n", x1, y1, x2, y2, width, height);

	/* This appears to clip the source box to what is actually going to be needed 
	 *  and also clip the dest box to where it should be displayed - excluding screen 
	 *  edge requirements */
	PXA3xxClipVideo(&dstBox, &x1, &x2, &y1, &y2, 
		REGION_EXTENTS(pScreen, clipBoxes), width, height);

	drw_w = (dstBox.x2 - dstBox.x1); /* The new target width/weight on screen */
	drw_h = (dstBox.y2 - dstBox.y1);

//	dbg("dstBox: (%i,%i)-(%i,%i)\n", dstBox.x1, dstBox.y1, dstBox.x2, dstBox.y2);
//	dbg("srcClipped: (%i,%i)-(%i,%i)\n", x1, y1, x2, y2);

	/** Check there is actually something to display */
	if((x1 >= x2) || (y1 >= y2) || drw_w <= 0 || drw_h <= 0)
		return Success;


	/* Work out how big the whole src buffer is */
	switch(id) {
		case FOURCC_YV12:
		case FOURCC_I420:
			srcPitch = (width + 3) & ~3;
			size = (srcPitch * height * 3) / 2;
			break;
/*		case FOURCC_UYVY:
		case FOURCC_YUY2:
			srcPitch = (width << 1);
			size = srcPitch * height;
//			dstPitch = (srcPitch + 7) & ~7;
//			size = dstPitch * height;
			break;
*/
		default:
			dbg("VID: Unsupported fourcc '%i'\n", id);	
			return -1;
	}

	/* The GFX might still be working from the last offscreen area
	 * so make sure everything is done.
	 * FIXME: do something with failure here. Will need to fallback. */
	pxa3xxGFX_Wait_Complete();

	/* Next, allocate an area offscreen in video memory that we can
	 * get the GFX controller to rescale from (and use/free the last one
	 * as necessary). */
	if (pPortPriv->off_screen != NULL && size != pPortPriv->size) {
		KdOffscreenFree(screen->pScreen, pPortPriv->off_screen);
		pPortPriv->off_screen = 0;
	}

	if (pPortPriv->off_screen == NULL) {
		/* FIXME: Check alignment value passed here,
		 * it probaly bigger than need be */
		pPortPriv->off_screen = KdOffscreenAlloc(screen->pScreen,
				size, 64, TRUE, PXA3xxVideoSave, pPortPriv);
		if (pPortPriv->off_screen == NULL){
			dbg("VID: Offscreen alloc for video temp buffer failed."
				"Requested %i bytes for %ix%i pitch %i id %i\n",
				size, width, height, srcPitch, id);
			return BadAlloc;
		}
	}

	if (pDraw->type == DRAWABLE_WINDOW)
		pPortPriv->pPixmap =
		    (*pScreen->GetWindowPixmap)((WindowPtr)pDraw);
	else
		pPortPriv->pPixmap = (PixmapPtr)pDraw;

	/* Migrate the pixmap to offscreen if necessary. */
	if (!kaaPixmapIsOffscreen(pPortPriv->pPixmap))
		kaaMoveInPixmap(pPortPriv->pPixmap);

	if (!kaaPixmapIsOffscreen(pPortPriv->pPixmap)) {
		dbg("VID: pixmap is still not offscreen.\n"); /* whatever that means */
		return BadAlloc;
	}

	pPortPriv->tmp_offset = pPortPriv->off_screen->offset;
	pPortPriv->tmp_virt = (CARD8 *)(pScreenPriv->screen->memory_base +
					pPortPriv->tmp_offset);
	pPortPriv->tmp_pitch = srcPitch;
	pPortPriv->size = size;
	pPortPriv->pDraw = pDraw;

	pPortPriv->id = id;
	pPortPriv->tmp_x1 = x1;		/* Store where to copy from in that buffer */
	pPortPriv->tmp_y1 = y1;
	pPortPriv->tmp_x2 = x2;
	pPortPriv->tmp_y2 = y2;
	pPortPriv->tmp_w = width;
	pPortPriv->tmp_h = height;

	/* Now copy all the data from the source to our onscreen buffer	*/
	memcpy(pPortPriv->tmp_virt, buf, size);

	/* Now sort out the overlay... */

	/* Funny sizes get past the driver and kill the LCD/DMA
	 * altogether without it noticing so double check here. 
	 * They should be dealth with by PXA3xxClipVideo(). */
	if( ((drw_w) & 0x03) || ((drw_h) & 0x03) ){
		dbg("VID: ******** FATAL: Nasty overlay size! It would have broken thet LCD. ********");
		exit(0);
	}

	/* Check that this is the same size as before, if not change the overlay */
	checkOverlay(&vidPriv->ov[1], dstBox.x1, dstBox.y1, drw_w, drw_h, 4, 16);

	pPortPriv->ov_w = drw_w;
	pPortPriv->ov_h = drw_h;
	pPortPriv->ov_pitch = vidPriv->ov[1].fix.line_length;

	return PXA3xxDisplayVideo(screen, pPortPriv);

}

/** Actually display the video from the temp buffer. Preferably using
 * the 2D graphics acceleration. */
int PXA3xxDisplayVideo(KdScreenInfo *screen, PXA3xxPortPrivPtr pPortPriv){
	struct pxa3xxVideoPriv *vidPriv = globalVPriv;
	int top, left, npixels, nlines;
	int x1, y1, x2, y2, chromaPlaneSize;
	unsigned long ovYPhys, ovUPhys, ovVPhys;
	unsigned long tmpYPhys, tmpUPhys, tmpVPhys;
	int stretch;

	startOp(OP_VIDEO);

	x1 = pPortPriv->tmp_x1;
	y1 = pPortPriv->tmp_y1;
	x2 = pPortPriv->tmp_x2;
	y2 = pPortPriv->tmp_y2;

	/* copy data from the tmp buffer to the overlay */
	top = y1 >> 16;
	left = (x1 >> 16) & ~1;
	npixels = ((((x2 + 0xffff) >> 16) + 1) & ~1) - left;
	nlines = ((((y2 + 0xffff) >> 16) + 1) & ~1) - top; /* I420 only*/	

	if(!isGFXEnabled()){
		dbg("VID: Gfx Not enabled, falling back\n");
		goto fallback;
	}

	if (pPortPriv->ov_w >= npixels && pPortPriv->ov_h >= nlines )
		stretch = TRUE;
	else if (pPortPriv->ov_w < npixels && pPortPriv->ov_h < nlines )
		stretch = FALSE;
	else{ 
		/* According to the docs we can't do a stretch in one dir
		 * and shrink in the other. However, deceminate seems to work. */
		stretch = FALSE;
		//dbg("part stretch part shrink - falling back\n");
		//goto fallback;
	}

	if( getOverlayPlanesPhys(&vidPriv->ov[1], &ovYPhys, &ovUPhys, &ovVPhys) < 0 ){
		dbg("VID: Unable to get overlay plane physical addresses\n");
		/* Fallback will fail aswell, so don't bother */
		endOp(OP_VIDEO);
		return -1;
	}

	chromaPlaneSize = (pPortPriv->tmp_pitch * pPortPriv->tmp_h) >> 2;
	tmpYPhys = getPhysAddrFromOffset(pPortPriv->tmp_offset);
	if(pPortPriv->id == FOURCC_I420){
		tmpUPhys = tmpYPhys + (chromaPlaneSize << 2);
		tmpVPhys = tmpUPhys + chromaPlaneSize;
	}else{ /* YV12 */
		tmpVPhys = tmpYPhys + (chromaPlaneSize << 2);
		tmpUPhys = tmpVPhys + chromaPlaneSize;
	}

	/* Use the GFX routines to stretch the video. 
	 * Since it can't handle YUV, just do each plane
	 * and use the 8-bit 'colour lookup' pixel type. */

	/* Y Plane */
	if(pxa3xxGFXCmd_set_buff(GC_BUFFI_SOURCE0, tmpYPhys, 1, pPortPriv->tmp_pitch, PF_8B_LOOKUP ) < 0){ dbg("A"); goto fallback; }
	if(pxa3xxGFXCmd_set_buff(GC_BUFFI_DEST0, ovYPhys, 1, pPortPriv->ov_pitch, PF_8B_LOOKUP ) < 0){ dbg("B");  goto fallback; }
	if(stretch){
		if(pxa3xxGFXCmd_stretch_blt( left, top, npixels, nlines,  0, 0, pPortPriv->ov_w, pPortPriv->ov_h) < 0){ dbg("C");  goto fallback; }
	}else{
		if(pxa3xxGFXCmd_deceminate_blt( left, top, npixels, nlines,  0, 0, pPortPriv->ov_w, pPortPriv->ov_h) < 0){ dbg("D");  goto fallback; }
	}

	/* For U and V everything is halved */
	nlines = nlines >> 1;
	npixels = npixels >> 1;
	top = top >> 1;
	left = left >> 1;

	/* U Plane */
	if(pxa3xxGFXCmd_set_buff(GC_BUFFI_SOURCE0, tmpUPhys, 1, (pPortPriv->tmp_pitch >> 1), PF_8B_LOOKUP ) < 0){ dbg("E");  goto fallback; }
	if(pxa3xxGFXCmd_set_buff(GC_BUFFI_DEST0, ovUPhys, 1, (pPortPriv->ov_pitch >> 1), PF_8B_LOOKUP  < 0)){ dbg("F");  goto fallback; }
	if(stretch){
		if(pxa3xxGFXCmd_stretch_blt( left, top, npixels, nlines,  0, 0, (pPortPriv->ov_w >> 1), (pPortPriv->ov_h >> 1)) < 0){ dbg("G");  goto fallback; }
	}else{
		if(pxa3xxGFXCmd_deceminate_blt( left, top, npixels, nlines,  0, 0, (pPortPriv->ov_w >> 1), (pPortPriv->ov_h >> 1)) < 0){ dbg("H");  goto fallback; }
	}

	/* V Plane */
	if(pxa3xxGFXCmd_set_buff(GC_BUFFI_SOURCE0, tmpVPhys, 1, (pPortPriv->tmp_pitch >> 1), PF_8B_LOOKUP ) < 0){ dbg("I");  goto fallback; }
	if(pxa3xxGFXCmd_set_buff(GC_BUFFI_DEST0, ovVPhys, 1, (pPortPriv->ov_pitch >> 1), PF_8B_LOOKUP ) < 0){ dbg("J");  goto fallback; }
	if(stretch){
		if(pxa3xxGFXCmd_stretch_blt( left, top, npixels, nlines,  0, 0, (pPortPriv->ov_w >> 1), (pPortPriv->ov_h >> 1)) < 0){ dbg("K");  goto fallback; }
	}else{
		if(pxa3xxGFXCmd_deceminate_blt( left, top, npixels, nlines,  0, 0, (pPortPriv->ov_w >> 1), (pPortPriv->ov_h >> 1)) < 0){ dbg("L");  goto fallback; }
	}

	/* Don't wait for completion, we can be getting on with stuff... */

	endOp(OP_VIDEO);
	return Success;

fallback:
	pxa3xxGFX_Wait_Complete();

	/* Resort to just copying what we can in - it won't be scaled though */	

	switch(pPortPriv->id) {
	case FOURCC_YV12:
	case FOURCC_I420:
		if(npixels > pPortPriv->ov_w)
		npixels =  pPortPriv->ov_w;
		top &= ~1;
		nlines = ((((y2 + 0xffff) >> 16) + 1) & ~1) - top;
		if(nlines >  pPortPriv->ov_h)
			nlines =  pPortPriv->ov_h;
		PXA3xxCopyPlanarData(screen, pPortPriv->tmp_virt, 
					pPortPriv->tmp_pitch,
					(pPortPriv->ov_pitch >> 1), 
					pPortPriv->tmp_h,
					top,
					left,
					nlines, npixels,
					pPortPriv->id);
		break;
/*	case FOURCC_UYVY:
	case FOURCC_YUY2:
		default:
		nlines = ((y2 + 0xffff) >> 16) - top;
		I810CopyPackedData(screen, buf, srcPitch, dstPitch, top, left, nlines, 
		npixels);
		break;
*/
	}

	endOp(OP_VIDEO);
	return Success;
}

/** Copy planar data from src buffer to overlay */
int PXA3xxCopyPlanarData(KdScreenInfo *screen, unsigned char *buf,
			int srcPitch, int dstPitch,  /* of chroma */
			int srcTotalHeight, int src_top, int src_left,
			int h, int w, int id ){
	struct pxa3xxVideoPriv *vidPriv = globalVPriv;

//	KdCardInfo *card = screen->card;
//	I810CardInfo	*i810c = (I810CardInfo *) card->driver;
//	I810PortPrivPtr pPriv = i810c->adaptor->pPortPrivates[0].ptr;

	int i;
	unsigned char *src1, *src2, *src3, *dst1, *dst2, *dst3;
	unsigned char *YPlane, *UPlane, *VPlane;
	if( getOverlayPlanes(&vidPriv->ov[1], &YPlane, &UPlane, &VPlane) < 0 ){
		dbg("VID: Unable to get overlay plane virtual addresses\n");
		return -1;
	}

	/* Copy Y data */
	src1 = buf + (src_top*srcPitch) + src_left;
	dst1 = YPlane;

	for (i = 0; i < h; i++) {
		memcpy(dst1, src1, w);
		src1 += srcPitch;
		dst1 += dstPitch << 1;
	}

	/* Copy V data for YV12, or U data for I420 */
	src2 = buf + (srcTotalHeight*srcPitch) + ((src_top*srcPitch)>>2) + (src_left>>1);
	if (id == FOURCC_I420)
		dst2 = UPlane;
	else
		dst2 = VPlane;
	
	for (i = 0; i < h/2; i++) {
		memcpy(dst2, src2, w/2);
		src2 += srcPitch>>1;
		dst2 += dstPitch;
	}

	/* Copy U data for YV12, or V data for I420 */
	src3 = buf + (srcTotalHeight*srcPitch) + ((srcTotalHeight*srcPitch)>>2) + ((src_top*srcPitch)>>2) + (src_left>>1);
	if (id == FOURCC_I420) 
		dst3 = VPlane;
	else
		dst3 = UPlane;

	for (i = 0; i < h/2; i++) {
		memcpy(dst3, src3, w/2);
		src3 += srcPitch>>1;
		dst3 += dstPitch;
	}

	return 0;
}


/* PXA3xxClipVideo -  
 *
 * 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.
 *
 * Also copes with limitations of overlay hardware/driver.
*/
void PXA3xxClipVideo(BoxPtr dst, INT32 *x1, INT32 *x2, INT32 *y1, INT32 *y2, 
	BoxPtr extents, INT32 width, INT32 height) {
	INT32 vscale, hscale, delta;
	int diff;

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

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

	/** Clip dst to extents and adjust src accordingly */
	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;     
	}

	/** Clip src to src edges and adjust dst accordingly */
	if(*x1 < 0) {
		diff =  (- *x1 + hscale - 1)/ hscale;
		dst->x1 += diff;
		*x1 += diff * hscale;
	}
	delta = *x2 - (width << 16);
	if(delta > 0) {
		diff = (delta + hscale - 1)/ hscale;
		dst->x2 -= diff;
		*x2 -= diff * hscale;
	}
	if(*y1 < 0) {
		diff =  (- *y1 + vscale - 1)/ vscale;
		dst->y1 += diff;
		*y1 += diff * vscale;
	}
	delta = *y2 - (height << 16);
	if(delta > 0) {
		diff = (delta + vscale - 1)/ vscale;
		dst->y2 -= diff;
		*y2 -= diff * vscale;
	}

	/* The driver will round UP the overlay to the nearest
	 * multiple of 16 pixels in width as per the h/w requirement.
	 * We don't want it to overlap edges, so make sure its 
	 * rounded down here. */
	diff = (dst->x2 - dst->x1) & 0x0F;
	if(diff > 0) {
		dst->x2 -= diff;
		*x2 -= diff * hscale;
	}

	/** The PXA LCD overlay hardware breaks horribly if the height
	 * isn't divisible by 2, so 'clip' to this aswell - it will 
	 * just give them a slightly smaller window. It does actually
	 * display if tried but then breaks when you try to change
	 * or disable it. */
	diff = (dst->y2 - dst->y1) & 0x03;
	if(diff > 0) {
		dst->y2 -= diff;
		*y2 -= diff * vscale;
	}
} 

/** I'm guessing the idea is that we can redraw the same image
 * a different place.
 * For us, this means recalculating the clipping etc, probably 
 * recreating the overlay and then calling DrawVideo again.
 */
int PXA3xxReputImage(KdScreenInfo *screen, DrawablePtr pDraw, short drw_x, short drw_y,
		RegionPtr clipBoxes, pointer data) {
	return BadMatch;	// Not done yet

/*	ScreenPtr pScreen = screen->pScreen;
	KdScreenPriv(pScreen);
//	ATICardInfo(pScreenPriv);
	PXA3xxPortPrivPtr pPortPriv = (PXA3xxPortPrivPtr)data;
	BoxPtr pOldExtents = REGION_EXTENTS(screen->pScreen, &pPortPriv->clip);
	BoxPtr pNewExtents = REGION_EXTENTS(screen->pScreen, clipBoxes);

	if (pOldExtents->x1 != pNewExtents->x1 ||
		pOldExtents->x2 != pNewExtents->x2 ||
		pOldExtents->y1 != pNewExtents->y1 ||
		pOldExtents->y2 != pNewExtents->y2)
		return BadMatch;

	INT32 x1, x2, y1, y2;
	int srcPitch;
	int size;
	BoxRec dstBox;

	/* 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;

	/* This appears to clip the source box to what is actually going to be needed 
	 *  and also clip the dest box to where it should be displayed - excluding screen 
	 *  edge requirements *
	PXA3xxClipVideo(&dstBox, &x1, &x2, &y1, &y2, 
		REGION_EXTENTS(pScreen, clipBoxes), width, height);

	drw_w = (dstBox.x2 - dstBox.x1); /* The new target width/weight on screen *
	drw_h = (dstBox.y2 - dstBox.y1);

//	dbg("dstBox: (%i,%i)-(%i,%i)\n", dstBox.x1, dstBox.y1, dstBox.x2, dstBox.y2);
//	dbg("srcClipped: (%i,%i)-(%i,%i)\n", x1, y1, x2, y2);

	/** Check there is actually something to display *
	if((x1 >= x2) || (y1 >= y2))
		return Success;

	if (pDraw->type == DRAWABLE_WINDOW)
		pPortPriv->pPixmap =
			(*pScreen->GetWindowPixmap)((WindowPtr)pDraw);
	else
		pPortPriv->pPixmap = (PixmapPtr)pDraw;

	if (!kaaPixmapIsOffscreen(pPortPriv->pPixmap))
		kaaMoveInPixmap(pPortPriv->pPixmap);

	if (!kaaPixmapIsOffscreen(pPortPriv->pPixmap)) {
		dbg("VID: pixmap is still not offscreen.\n"); /* whatever that means *
		return BadAlloc;
	}

	/* update cliplist *
	if (!REGION_EQUAL(screen->pScreen, &pPortPriv->clip, clipBoxes))
		REGION_COPY(screen->pScreen, &pPortPriv->clip, clipBoxes);

	/* Now sort out the overlay... *

	/* Funny sizes get past the driver and kill the LCD/DMA
	 * altogether without it noticing so double check here. 
	 * They should be dealth with by PXA3xxClipVideo(). *
	if( ((drw_w) & 0x03) || ((drw_h) & 0x03) ){
		dbg("VID: ******** FATAL: Nasty overlay size! It would have broken thet LCD. ********");
		exit(0);
	}

	/* Check that this is the same size as before, if not change the overlay *
	checkOverlay(&vidPriv->ov[1], dstBox.x1, dstBox.y1, drw_w, drw_h, 4, 16);

	pPortPriv->ov_w = drw_w;
	pPortPriv->ov_h = drw_h;
	pPortPriv->ov_pitch = vidPriv->ov[1].fix.line_length;

	return PXA3xxDisplayVideo(screen, pPortPriv);
*/
}


int PXA3xxQueryImageAttributes(KdScreenInfo *screen, int id, unsigned short *w,
		unsigned short *h, int *pitches, int *offsets){ 
	int size, tmp;
//	dbg("VID: +-PXA3xxQueryImageAttributes()\n");
	
	if (*w > IMAGE_MAX_WIDTH) 
		*w = IMAGE_MAX_WIDTH;
	if (*h > IMAGE_MAX_HEIGHT) 
		*h = IMAGE_MAX_HEIGHT;

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

	/* According to mplayer, we can support YV12, IYUV and I420 */
//	dbg("VID: FOURCC = ");	
	switch (id)
	{
	case FOURCC_YV12:
//		dbg("FOURCC_YV12 ");	
	case FOURCC_I420:
//		dbg("FOURCC_I420 ");	

		/* No idea what all this does */
		*h = (*h + 1) & ~1;
		*w = (*w + 15) & ~15;
		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;

	default:
		dbg("Unsupported fourcc (%i)\n", id);
		return -1;
	}
/*
	case FOURCC_UYVY:
		dbg("FOURCC_UYVY ");	
	case FOURCC_YUY2:
		dbg("FOURCC_YUY2 ");	
	default:
		dbg("FOURCC_other ");	
		size = *w << 1;
		if (pitches) 
			pitches[0] = size;
		size *= *h;
		break;
	}*/

//	dbg("\n");

	return size;
}

KdVideoAdaptorPtr PXA3xxSetupImageVideo(ScreenPtr pScreen) {
	struct pxa3xxVideoPriv *vidPriv = globalVPriv;
	KdScreenPriv(pScreen);
//	ATIScreenInfo(pScreenPriv);
	PXA3xxPortPrivPtr pPortPriv;
	KdVideoAdaptorPtr adapt;
	int i;

	vidPriv->num_texture_ports = 1; //FIXME: set from somewhere

	adapt = xcalloc(1, sizeof(KdVideoAdaptorRec) + vidPriv->num_texture_ports *
	    (sizeof(PXA3xxPortPrivRec) + sizeof(DevUnion)));

	if (adapt == NULL){
		dbg("VID: Failed to allocate mem for KdVideoAdaptorPtr\n");
		return NULL;
	}

	adapt->type = XvWindowMask | XvInputMask | XvImageMask;
	adapt->flags = VIDEO_OVERLAID_IMAGES | VIDEO_CLIP_TO_VIEWPORT;
	adapt->name = "PXA3xx Video";
	adapt->nEncodings = 1;
	adapt->pEncodings = DummyEncoding;
	adapt->nFormats = NUM_FORMATS;
	adapt->pFormats = Formats;
	adapt->nPorts = vidPriv->num_texture_ports;
	adapt->pPortPrivates = (DevUnion*)(&adapt[1]);

	pPortPriv =
	    (PXA3xxPortPrivPtr)(&adapt->pPortPrivates[vidPriv->num_texture_ports]);

	for (i = 0; i < vidPriv->num_texture_ports; i++)
		adapt->pPortPrivates[i].ptr = &pPortPriv[i];

	adapt->nAttributes = NUM_ATTRIBUTES;
	adapt->pAttributes = Attributes;
	adapt->pImages = Images;
	adapt->nImages = NUM_IMAGES;
	adapt->PutVideo = NULL;
	adapt->PutStill = NULL;
	adapt->GetVideo = NULL;
	adapt->GetStill = NULL;
	adapt->StopVideo = PXA3xxStopVideo;
	adapt->SetPortAttribute = PXA3xxSetPortAttribute;
	adapt->GetPortAttribute = PXA3xxGetPortAttribute;
	adapt->QueryBestSize = PXA3xxQueryBestSize;
	adapt->PutImage = PXA3xxPutImage;
	adapt->ReputImage = PXA3xxReputImage;
	adapt->QueryImageAttributes = PXA3xxQueryImageAttributes;

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

	vidPriv->pAdaptor = adapt;

	xvBrightness = MAKE_ATOM("XV_BRIGHTNESS");
	xvSaturation = MAKE_ATOM("XV_SATURATION");

	return adapt;
}


Bool PXA3xxInitVideo(ScreenPtr pScreen) {
	struct pxa3xxVideoPriv *vidPriv = globalVPriv;

	KdScreenPriv(pScreen);
	KdScreenInfo *screen = pScreenPriv->screen;
	KdVideoAdaptorPtr *adaptors, *newAdaptors = NULL;
	KdVideoAdaptorPtr newAdaptor = NULL;
	int num_adaptors;
	dbg("+VID: PXA3xxInitVideo()\n");

	globalVPriv = (struct pxa3xxVideoPriv *)xalloc(sizeof(struct pxa3xxVideoPriv));
	if(!globalVPriv){
		dbg("VID: Unable to alloc memory for pxa3xxVideoPriv\n");
		return FALSE;
	}
	vidPriv = globalVPriv;
	memset(vidPriv, 0, sizeof(struct pxa3xxVideoPriv));

	vidPriv->pAdaptor = NULL;

	num_adaptors = KdXVListGenericAdaptors(screen, &adaptors);

	newAdaptor = PXA3xxSetupImageVideo(pScreen);

	if (newAdaptor)  {
		if (!num_adaptors) {
			num_adaptors = 1;
			adaptors = &newAdaptor;
		} else {
			newAdaptors = xalloc((num_adaptors + 1) * 
			    sizeof(KdVideoAdaptorPtr *));
			if (newAdaptors) {
				memcpy(newAdaptors, adaptors, num_adaptors *
				    sizeof(KdVideoAdaptorPtr));
				newAdaptors[num_adaptors] = newAdaptor;
				adaptors = newAdaptors;
				num_adaptors++;
			}
		}
	}

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

	if (newAdaptors)
		xfree(newAdaptors);


	vidPriv->ov[0].name = "/dev/fb1";
	vidPriv->ov[0].num = 1;
	vidPriv->ov[1].name = "/dev/fb2";
	vidPriv->ov[1].num = 2;

/*
	if(!openOverlay(&vidPriv->ov[0]))
		return FALSE;
	if(!openOverlay(&vidPriv->ov[1]))
		return FALSE;
*/

//	setupOverlay(&vidPriv->ov[0], 0, 0, 50, 100, 0, 16);
//	setupOverlay(&vidPriv->ov[1], 100, 100, 200, 200, 4, 16);

	dbg("-VID: PXA3xxInitVideo(): OK\n");
	return TRUE;
}

void PXA3xxFiniVideo(ScreenPtr pScreen) {
	struct pxa3xxVideoPriv *vidPriv = globalVPriv;
	KdScreenPriv(pScreen);
	//ATIScreenInfo(pScreenPriv);
	KdVideoAdaptorPtr adapt = vidPriv->pAdaptor;
	PXA3xxPortPrivPtr pPortPriv;
	int i;

	dbg("+VID: PXA3xxFiniVideo\n");

	if (!adapt)
		return;

	for (i = 0; i < vidPriv->num_texture_ports; i++) {
		pPortPriv = (PXA3xxPortPrivPtr)(&adapt->pPortPrivates[i].ptr);
		REGION_UNINIT(pScreen, &pPortPriv->clip);
	}
	xfree(adapt);
	vidPriv->pAdaptor = NULL;

	for(i=0;i<2;i++){
		closeOverlay(&vidPriv->ov[i]);

	dbg("-VID: PXA3xxFiniVideo\n");
}

/******* Interfacing with kernel/Hardware functions ***********/

int openOverlay(struct pxa3xxOverlayInfo *ov){
	int ret1, ret2;

	if(ov->fd){
		dbg("VID: ov%i already inited\n", ov->num);
		return FALSE;
	}

	ov->fd = open(ov->name, O_RDWR );
	if(ov->fd < 0){
		dbg("VID: Failed to open '%s' for overlay %i framebuffer: %i\n",
			ov->name, ov->num, ov->fd);
		return FALSE;
	}

	//Get the overlay infos
	ret1 = ioctl( ov->fd, FBIOGET_VSCREENINFO, &(ov->var) );
	ret2 = ioctl( ov->fd, FBIOGET_FSCREENINFO, &(ov->fix) );
    	
	if( ret1 < 0 || ret2 < 0){
	    	dbg("VID: ioctl FBIOGET_[F/V]SCREENINFO to overlay %i failed: %i %i\n",
			ov->num, ret1, ret2);
		return FALSE;
	}

	ov->virt = NULL;

	dbg("VID: overlays opened OK\n");
	return TRUE;
}

int inline checkOverlay(struct pxa3xxOverlayInfo *ov, int x, int y, int w, int h,
			int format, int bpp){

	if(!ov->fd)
		openOverlay(ov);

	if(ov->virt && ov->x == x && ov->y == y &&
			ov->w == w && ov->h == h &&
			ov->format == format && ov->bpp == bpp)
		return TRUE;

//	dbg("VID: Moving overlay to (%i,%i) (%ix%i)\n", x, y, w, h);

	return setupOverlay(ov, x, y, w, h, format, bpp);
}

int setupOverlay(struct pxa3xxOverlayInfo *ov, int x, int y, int w, int h,
			int format, int bpp){
	int ret;

	/* Set our info invalid incase anything fails */
	ov->x = -1; ov->y = -1; ov->w = -1; ov->h = -1;
	ov->format = -1; ov->bpp = -1;	


	if(ov->virt){
		munmap(ov->virt, ov->fix.smem_len);
		ov->virt = NULL;
	}

	/* Get the var info so we can modify it */
	/*ret = ioctl( ov->fd, FBIOGET_VSCREENINFO, &ov->var );
	if(ret < 0){
		dbg("VID: Failed to get variable fb info for first time for"
			" overlay 2 (ioctl(FBIOGET_FSCREENINFO)): %i\n", ret);
		return FALSE;
	}*/

	/* Mainline kernel driver fails if we give back it's struct here */
	memset(&ov->var, 0, sizeof(struct fb_var_screeninfo));

	ov->var.xres = w;
	ov->var.yres = h;
	ov->var.nonstd = (format << 20) /* Format YV12 */
			| ( (x & 0x3ff) <<  0)  /* x position */
			| ( (y & 0x3ff) << 10); /* y position */ 

	/* We have to set the bits per pixel to a valid value, even though it is
	* incorrect for YV12 */
	ov->var.bits_per_pixel = bpp;

	/* Set the variable info to init the overlay memory */	
	ret = ioctl( ov->fd, FBIOPUT_VSCREENINFO, &(ov->var) );
	if(ret < 0){
		dbg("VID: Failed to set variable info for overlay 2"
			" (ioctl(FBIOPUT_VSCREENINFO)): %i\n", ret);
		return FALSE;
	}
	/* Get the fixed info again */
	ret = ioctl( ov->fd, FBIOGET_FSCREENINFO, &(ov->fix) );
	if(ret < 0){
		dbg("VID: Failed to get fixed fb info for overlay 2"
			" (ioctl(FBIOGET_FSCREENINFO)): %i\n", ret);
		return FALSE;
	}
	ov->phys = ov->fix.smem_start;

	/* Now we can map the memory */	
	ov->virt = mmap( NULL, ov->fix.smem_len, (PROT_READ | PROT_WRITE), MAP_SHARED, ov->fd, 0 );
	if(ov->virt == MAP_FAILED){
		dbg("VID: Failed mmap overlay 2.\n");
		return FALSE;
	}

	/* Finally, find the offsets of each plane by getting the var data again */
	ret = ioctl( ov->fd, FBIOGET_VSCREENINFO, &(ov->var) );
	if(ret < 0){
		dbg("VID: Failed to get variable fb info for second time"
			" for overlay 2 (ioctl(FBIOGET_FSCREENINFO)): %i\n", ret);
		munmap(ov->virt, ov->fix.smem_len);
		ov->virt = NULL;
		return FALSE;
	}

	/* Store the new settings so we know when the request changes */
	ov->x = x; ov->y = y; ov->w = w; ov->h = h;
	ov->format = format; ov->bpp = bpp;	

	return TRUE;
}

/** Gets the virtual addresses of the Y, U and V planes */
int inline getOverlayPlanes(struct pxa3xxOverlayInfo *ov,
		unsigned char **YPtr, unsigned char **UPtr, unsigned char **VPtr) {
	if( (ov->var.nonstd >> 20 & 0x07) == 0) /* RGB mode - no planes */
		return -1;

	if(!ov->virt)
		return -1;
	
	*YPtr = ((unsigned char *)ov->virt) + ov->var.red.offset;
	*UPtr = ((unsigned char *)ov->virt) + ov->var.green.offset;
	*VPtr = ((unsigned char *)ov->virt) + ov->var.blue.offset;

	return 0;
}

/** Gets the physical(dma) addresses of the Y, U and V planes */
int inline getOverlayPlanesPhys(struct pxa3xxOverlayInfo *ov,
		unsigned long *YPtr, unsigned long *UPtr, unsigned long *VPtr) {
	if( (ov->var.nonstd >> 20 & 0x07) == 0) /* RGB mode - no planes */
		return -1;

	if(!ov->virt)
		return -1;
	
	*YPtr = ov->phys + ov->var.red.offset;
	*UPtr = ov->phys + ov->var.green.offset;
	*VPtr = ov->phys + ov->var.blue.offset;

	return 0;
}

void closeOverlay(struct pxa3xxOverlayInfo *ov){
	
	if(ov->virt)
		munmap(ov->virt, ov->fix.smem_len);
	ov->virt = NULL;

	if(ov->fd)
		close(ov->fd);
	ov->fd = 0;

}


