/*

X11 video driver for the Conexant 23415 MPEG encoder/decoder On-Screen Display
using the ivtvfb framebuffer driver.

Copyright (C) 2004 Matthias Badaire
Copyright (C) 2004-2007 John P. Harvey <john.p.harvey@btinternet.com>
Copyright (C) 2006-2007 Ian Armstrong <ian@iarmst.demon.co.uk>

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, FIT-
NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
XFREE86 PROJECT 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.

Except as contained in this notice, the name of the XFree86 Project shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the XFree86 Project.
*/

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

#include "ivtv_hw.h"
#include "ivtv_xv.h"
#include "ivtv.h"

/* all drivers need this */
#include "xf86.h"
#include "xf86_OSproc.h"

#include "mipointer.h"
#include "mibstore.h"
#include "micmap.h"
#include "colormapst.h"
#include "xf86cmap.h"
#include "shadow.h"

/* for visuals */
#include "fb.h"

#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 6
# include "xf86Resources.h"
# include "xf86RAC.h"
#endif

#ifdef XvExtension
# include "xf86xv.h"
#endif

enum HAUPPAUGE_CHIPTAGS
{
    PVR_UNKNOWN = 0,
    PVR_350,
    PVR_LAST
};

#define PCI_CHIP_PVR350         0x0803

static SymTabRec IVTVChipsets[] = {
    {PVR_350, "PVR-350"},
    {-1, NULL}
};

/* This table maps a PCI device ID to a chipset family identifier. */
static PciChipsets IVTVPciChipsets[] = {
    {PVR_350, PCI_CHIP_PVR350, RES_UNDEFINED},
    {-1, -1, RES_UNDEFINED}
};

#if IVTVDEBUG
# define TRACE_ENTER(str)       ErrorF("ivtv: " str " %d\n",pScrn->scrnIndex)
# define TRACE_EXIT(str)        ErrorF("ivtv: " str " done\n")
# define TRACE(str)             ErrorF("ivtv trace: " str "\n")
#else
# define TRACE_ENTER(str)
# define TRACE_EXIT(str)
# define TRACE(str)
#endif

/* -------------------------------------------------------------------- */
/* prototypes                                                           */

static const OptionInfoRec *IVTVDevAvailableOptions(int chipid, int busid);
static void IVTVDevIdentify(int flags);
static Bool IVTVDevProbe(DriverPtr drv, int flags);
static Bool IVTVDevPreInit(ScrnInfoPtr pScrn, int flags);
static Bool IVTVDevScreenInit(int Index, ScreenPtr pScreen, int argc,
    char **argv);
static Bool IVTVDevCloseScreen(int scrnIndex, ScreenPtr pScreen);

/* -------------------------------------------------------------------- */

/*
 * This is intentionally screen-independent.  It indicates the binding
 * choice made in the first PreInit.
 */

static int pix24bpp = 0;

#define IVTVDEV_NAME		"IVTV"
#define IVTVDEV_DRIVER_NAME	"ivtv"
#define IVTVDEV_VERSION_MAJOR	1
#define IVTVDEV_VERSION_MINOR	1
#define IVTVDEV_VERSION_PATCH	2
#define IVTVDEV_VERSION	((IVTVDEV_VERSION_MAJOR << 16) | \
			(IVTVDEV_VERSION_MINOR << 8) | \
			(IVTVDEV_VERSION_PATCH))

_X_EXPORT DriverRec IVTVDEV = {
    IVTVDEV_VERSION,
    IVTVDEV_DRIVER_NAME,
    IVTVDevIdentify,
    IVTVDevProbe,
    IVTVDevAvailableOptions,
    NULL,
    0
};

/* Supported options */
typedef enum
{
    OPTION_IVTVDEV,
    OPTION_IVTVLACE
} IVTVDevOpts;

static const OptionInfoRec IVTVDevOptions[] = {
    {OPTION_IVTVDEV, "fbdev", OPTV_STRING, {0}, FALSE},
    {OPTION_IVTVLACE, "xv_interlace", OPTV_BOOLEAN, {0}, FALSE},
    {-1, NULL, OPTV_NONE, {0}, FALSE}
};

/* -------------------------------------------------------------------- */

static const char *fbSymbols[] = {
    "fbScreenInit",
    "fbPictureInit",
    NULL
};

static const char *shadowSymbols[] = {
    "shadowAlloc",
    "shadowInit",
    NULL
};

#ifdef XFree86LOADER
MODULESETUPPROTO(IVTVDevSetup);
# ifdef XORG_VERSION_CURRENT
#  define IVTV_X_VERSION XORG_VERSION_CURRENT
# else
#  define IVTV_X_VERSION XF86_VERSION_CURRENT
# endif

static XF86ModuleVersionInfo IVTVDevVersRec = {
    "ivtv",
    MODULEVENDORSTRING,
    MODINFOSTRING1,
    MODINFOSTRING2,
    IVTV_X_VERSION,
    IVTVDEV_VERSION_MAJOR, IVTVDEV_VERSION_MINOR, IVTVDEV_VERSION_PATCH,
    ABI_CLASS_VIDEODRV,
    ABI_VIDEODRV_VERSION,
    NULL,
    {0, 0, 0, 0}
};

_X_EXPORT XF86ModuleData ivtvModuleData = { &IVTVDevVersRec, IVTVDevSetup, NULL };

pointer
IVTVDevSetup(pointer module, pointer opts, int *errmaj, int *errmin)
{
    static Bool setupDone = FALSE;

    if (!setupDone) {
	setupDone = TRUE;
	xf86AddDriver(&IVTVDEV, module, 0);
#ifndef HAVE_XEXTPROTO_71
	LoaderRefSymLists(fbSymbols, shadowSymbols, NULL);
#endif
	return (pointer) 1;
    }
    if (errmaj)
	*errmaj = LDR_ONCEONLY;
    return NULL;
}

#endif /* XFree86LOADER */

static void
IVTVshadowUpdatePacked(ScreenPtr pScreen, shadowBufPtr pBuf)
{
#ifdef X_NEW_PARMS
    RegionPtr damage = DamageRegion(pBuf->pDamage);
#else
    RegionPtr damage = &pBuf->damage;
#endif

    int nbox = REGION_NUM_RECTS(damage);
    BoxPtr pbox = REGION_RECTS(damage);
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    IVTVDevPtr devPtr = IVTVDEVPTR(pScrn);
    int x1 = pScrn->virtualX;
    int x2 = 0;
    int y1 = pScrn->virtualY;
    int y2 = 0;

#ifdef IVTVDEVHW_BYTE_SWAP
    switch (pScrn->bitsPerPixel) {
    case 32: {
	unsigned long *display;
	int x, y, shadow_offset;

	while (nbox--) {
	    for (y = pbox->y1; y < pbox->y2; y++) {
		shadow_offset = (y * devPtr->shadow_width) + (pbox->x1 * 4);
		display = devPtr->fbmem + shadow_offset;

		for (x = 0; x < pbox->x2 - pbox->x1; x++) {
		    display[x] = (devPtr->shadowmem[shadow_offset + 0]) |
			(devPtr->shadowmem[shadow_offset + 1] << 8) |
			(devPtr->shadowmem[shadow_offset + 2] << 16) |
			(devPtr->shadowmem[shadow_offset + 3] << 24);
		    shadow_offset += 4;
		}
	    }
	    pbox++;
	}
	return;
    }

    case 16: {
	unsigned short *display;
	int x, y, shadow_offset;

	while (nbox--) {
	    for (y = pbox->y1; y < pbox->y2; y++) {
		shadow_offset = (y * devPtr->shadow_width) + (pbox->x1 * 2);
		display = devPtr->fbmem + shadow_offset;

		for (x = 0; x < pbox->x2 - pbox->x1; x++) {
		    display[x] = (devPtr->shadowmem[shadow_offset + 0]) |
			(devPtr->shadowmem[shadow_offset + 1] << 8);
		    shadow_offset += 2;
		}
	    }
	    pbox++;
	}
	return;
    }
    }
#endif /* IVTVDEVHW_BYTE_SWAP */

    while (nbox--) {
	if (pbox->x1 < x1)
	    x1 = pbox->x1;
	if (pbox->x2 > x2)
	    x2 = pbox->x2;
	if (pbox->y1 < y1)
	    y1 = pbox->y1;
	if (pbox->y2 > y2)
	    y2 = pbox->y2;
	pbox++;
    }

    ivtvHWSendDMA(pScrn, devPtr->shadowmem, x1, x2, y1, y2);
}

static Bool
IVTVDevGetRec(ScrnInfoPtr pScrn)
{
    if (pScrn->driverPrivate != NULL)
	return TRUE;

    pScrn->driverPrivate = xnfcalloc(sizeof(IVTVDevRec), 1);
    return TRUE;
}

static void
IVTVDevFreeRec(ScrnInfoPtr pScrn)
{
    if (pScrn->driverPrivate == NULL)
	return;
    free(pScrn->driverPrivate);
    pScrn->driverPrivate = NULL;
}

static const OptionInfoRec *
IVTVDevAvailableOptions(int chipid, int busid)
{
    return IVTVDevOptions;
}

static void
IVTVDevIdentify(int flags)
{
    xf86PrintChipsets(IVTVDEV_NAME, "driver for ivtv framebuffer",
	IVTVChipsets);
}

static Bool
IVTVDevProbe(DriverPtr drv, int flags)
{
    int i;
    GDevPtr *devSections;
    int numDevSections;
    char *dev;
    Bool foundScreen = FALSE;
    int *usedChips;
    int numUsed;

    TRACE("probe start");

    /* For now, just bail out for PROBE_DETECT. */
    if (flags & PROBE_DETECT)
	return FALSE;

    /* sanity checks */
    if ((numDevSections =
	    xf86MatchDevice(IVTVDEV_DRIVER_NAME, &devSections)) <= 0)
	return FALSE;

    numUsed = xf86MatchPciInstances(IVTVDEV_DRIVER_NAME,
	PCI_HAUPPAUGE_VENDOR_ID,
	IVTVChipsets,
	IVTVPciChipsets, devSections, numDevSections, drv, &usedChips);

    if (numUsed <= 0)
	return FALSE;

    if (flags & PROBE_DETECT) {
	free(devSections);
	TRACE("probe done");
	return TRUE;
    }

    for (i = 0; i < numUsed; i++) {
	ScrnInfoPtr pScrn = xf86AllocateScreen(drv, 0);

	dev = xf86FindOptionValue(devSections[i]->options, "fbdev");
	if (!ivtvHWProbe(NULL, dev, NULL))
	    continue;

	pScrn = xf86ConfigPciEntity(pScrn, 0, usedChips[i],
		IVTVPciChipsets, 0, 0, 0, 0, 0);
	
	if (pScrn == NULL)
	    continue;

	/* xf86DrvMsg() can't be called without setting these */
	pScrn->driverName = IVTVDEV_DRIVER_NAME;
	pScrn->name = IVTVDEV_NAME;
	pScrn->driverVersion = IVTVDEV_VERSION;
	pScrn->driverName = IVTVDEV_DRIVER_NAME;
	pScrn->name = IVTVDEV_NAME;
	pScrn->Probe = IVTVDevProbe;
	pScrn->PreInit = IVTVDevPreInit;
	pScrn->ScreenInit = IVTVDevScreenInit;
	pScrn->SwitchMode = ivtvHWSwitchMode;
	pScrn->AdjustFrame = ivtvHWAdjustFrame;
	pScrn->EnterVT = ivtvHWEnterVT;
	pScrn->LeaveVT = ivtvHWLeaveVT;
	pScrn->ValidMode = ivtvHWValidMode;
	foundScreen = TRUE;

	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
		"using %s\n", dev ? dev : "default device");
    }
    free(devSections);
    TRACE("probe done");
    return foundScreen;
}

static Bool
IVTVDevPreInit(ScrnInfoPtr pScrn, int flags)
{
    IVTVDevPtr devPtr;
    int default_depth, fbbpp;
    const char *mod = NULL;
    const char **syms = NULL;

    if (flags & PROBE_DETECT)
	return FALSE;

    TRACE_ENTER("PreInit");

    /* Check the number of entities, and fail if it isn't one. */
    if (pScrn->numEntities != 1)
	return FALSE;

    pScrn->monitor = pScrn->confScreen->monitor;

    IVTVDevGetRec(pScrn);
    devPtr = IVTVDEVPTR(pScrn);

    devPtr->pEnt = xf86GetEntityInfo(pScrn->entityList[0]);

#ifndef XSERVER_LIBPCIACCESS
    pScrn->racMemFlags = RAC_FB | RAC_COLORMAP | RAC_CURSOR | RAC_VIEWPORT;
    /* XXX Is this right?  Can probably remove RAC_FB */
    pScrn->racIoFlags = RAC_FB | RAC_COLORMAP | RAC_CURSOR | RAC_VIEWPORT;

    if (devPtr->pEnt->location.type == BUS_PCI &&
	xf86RegisterResources(devPtr->pEnt->index, NULL, ResExclusive)) {
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
	    "DevPreInit: xf86RegisterResources() found resource conflicts\n");
	return FALSE;
    }
#endif

    /* open device */
    if (!ivtvHWInit(pScrn, NULL,
	    xf86FindOptionValue(devPtr->pEnt->device->options, "fbdev")))
	return FALSE;
    default_depth = ivtvHWGetDepth(pScrn, &fbbpp);
    if (!xf86SetDepthBpp(pScrn, default_depth, default_depth, fbbpp, 0))
	return FALSE;
    xf86PrintDepthBpp(pScrn);

    /* Get the depth24 pixmap format */
    if (pScrn->depth == 24 && pix24bpp == 0)
	pix24bpp = xf86GetBppFromDepth(pScrn, 24);

    /* color weight */
    if (pScrn->depth > 8) {
	rgb zeros = { 0, 0, 0 };
	if (!xf86SetWeight(pScrn, zeros, zeros))
	    return FALSE;
    }

    /* visual init */
    if (!xf86SetDefaultVisual(pScrn, -1))
	return FALSE;

    /* We don't currently support DirectColor at > 8bpp */
    if (pScrn->depth > 8 && pScrn->defaultVisual != TrueColor) {
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "DevPreInit: "
		"Given default visual (%s) is not supported at depth %d\n",
		xf86GetVisualName(pScrn->defaultVisual), pScrn->depth);
	return FALSE;
    }

    Gamma zeros = { 0.0, 0.0, 0.0 };

    if (!xf86SetGamma(pScrn, zeros)) {
	return FALSE;
    }

    pScrn->progClock = TRUE;
    pScrn->rgbBits = 8;
    pScrn->chipset = "ivtv";
    pScrn->videoRam = ivtvHWGetVidmem(pScrn);

    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Hardware: %s (vidmem: %dk)\n",
	ivtvHWGetName(pScrn), pScrn->videoRam / 1024);

    /* handle options */
    xf86CollectOptions(pScrn, NULL);
    if (!(devPtr->Options = malloc(sizeof(IVTVDevOptions))))
	return FALSE;
    memcpy(devPtr->Options, IVTVDevOptions, sizeof(IVTVDevOptions));
    xf86ProcessOptions(pScrn->scrnIndex, devPtr->pEnt->device->options,
	devPtr->Options);

    devPtr->xv_interlaceControl = FALSE;

    if (xf86IsOptionSet(devPtr->Options, OPTION_IVTVLACE))
	xf86GetOptValBool(devPtr->Options, OPTION_IVTVLACE, &devPtr->xv_interlaceControl);

    xf86ShowUnusedOptions(pScrn->scrnIndex, devPtr->pEnt->device->options);

    /* select video modes */

    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
	    "Checking Modes against framebuffer device...\n");
    ivtvHWSetVideoModes(pScrn);

    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
	    "Checking Modes against monitor...\n");

    if (pScrn->modes != NULL) {
	DisplayModePtr mode, first;

	first = mode = pScrn->modes;
	do {
	    mode->status = xf86CheckModeForMonitor(mode, pScrn->monitor);
	    mode = mode->next;
	} while (mode != NULL && mode != first);
    }

    xf86PruneDriverModes(pScrn);

    if (NULL == pScrn->modes)
	ivtvHWUseBuildinMode(pScrn);

    pScrn->currentMode = pScrn->modes;
    pScrn->displayWidth = pScrn->virtualX;	/* ShadowFB handles this correctly */

    /* The allocated shadow buffer will be this big */
    devPtr->shadow_width = pScrn->virtualX * (pScrn->bitsPerPixel / 8);
    devPtr->shadow_height = pScrn->virtualY;

    xf86PrintModes(pScrn);

    /* Set display resolution */
    xf86SetDpi(pScrn, 0, 0);

    /* Load bpp-specific modules */
    mod = "fb";
    syms = fbSymbols;

    if (mod && xf86LoadSubModule(pScrn, mod) == NULL) {
	IVTVDevFreeRec(pScrn);
	return FALSE;
    }

#ifndef HAVE_XEXTPROTO_71
    if (mod && syms) {
	xf86LoaderReqSymLists(syms, NULL);
    }
#endif

    /* Load shadow */
    xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Using \"Shadow Framebuffer\"\n");
    if (!xf86LoadSubModule(pScrn, "shadow")) {
	IVTVDevFreeRec(pScrn);
	return FALSE;
    }

#ifndef HAVE_XEXTPROTO_71
    xf86LoaderReqSymLists(shadowSymbols, NULL);
#endif

    TRACE_EXIT("PreInit");
    return TRUE;
}

static Bool
IVTVDevCreateScreenResources(ScreenPtr pScreen)
{
    PixmapPtr pPixmap;
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    IVTVDevPtr devPtr = IVTVDEVPTR(pScrn);
    Bool ret;

    pScreen->CreateScreenResources = devPtr->CreateScreenResources;
    ret = pScreen->CreateScreenResources(pScreen);
    pScreen->CreateScreenResources = IVTVDevCreateScreenResources;

    if (!ret)
	return FALSE;

    pPixmap = pScreen->GetScreenPixmap(pScreen);

    if (!shadowAdd(pScreen, pPixmap, IVTVshadowUpdatePacked, NULL, 0, NULL)) {
	return FALSE;
    }

    return TRUE;
}

static Bool
IVTVDevShadowInit(ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    IVTVDevPtr devPtr = IVTVDEVPTR(pScrn);

    if (!shadowSetup(pScreen)) {
	return FALSE;
    }

    devPtr->CreateScreenResources = pScreen->CreateScreenResources;
    pScreen->CreateScreenResources = IVTVDevCreateScreenResources;

    return TRUE;
}

static Bool
IVTVDevScreenInit(int scrnIndex, ScreenPtr pScreen, int argc, char **argv)
{
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    IVTVDevPtr devPtr = IVTVDEVPTR(pScrn);
    VisualPtr visual;
    int ret, flags, width, height;

    TRACE_ENTER("IVTVDevScreenInit");

#ifdef IVTVDEVHW_BYTE_SWAP
    if (ivtvHWMapVidmem(pScrn) == NULL)
	return FALSE;
    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Byte-swapping enabled\n");
#endif

    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
	"bitsPerPixel=%d, depth=%d, defaultVisual=%s\n"
	"\tmask: %x,%x,%x, offset: %d,%d,%d\n", pScrn->bitsPerPixel,
	pScrn->depth, xf86GetVisualName(pScrn->defaultVisual),
	(int)pScrn->mask.red, (int)pScrn->mask.green, (int)pScrn->mask.blue,
	(int)pScrn->offset.red, (int)pScrn->offset.green, (int)pScrn->offset.blue);

    ivtvHWSave(pScrn);

    if (!ivtvHWModeInit(pScrn, pScrn->currentMode)) {
	xf86DrvMsg(scrnIndex, X_ERROR, "DevScreenInit: Mode init failed\n");
	return FALSE;
    }
    ivtvHWSaveScreen(pScreen, SCREEN_SAVER_ON);
    ivtvHWAdjustFrame(scrnIndex, 0, 0, 0);

    /* mi layer */
    miClearVisualTypes();
    if (pScrn->bitsPerPixel > 8) {
	if (!miSetVisualTypes
	    (pScrn->depth, TrueColorMask, pScrn->rgbBits, TrueColor)) {
	    xf86DrvMsg(scrnIndex, X_ERROR,
			"DevScreenInit: Set >8bpp visual types failed\n");
	    return FALSE;
	}
    } else if (!miSetVisualTypes(pScrn->depth,
		miGetDefaultVisualMask(pScrn->depth),
		pScrn->rgbBits, pScrn->defaultVisual)) {
	xf86DrvMsg(scrnIndex, X_ERROR,
		    "DevScreenInit: Set visual types failed\n");
	return FALSE;
    }
    if (!miSetPixmapDepths()) {
	xf86DrvMsg(scrnIndex, X_ERROR,
		    "DevScreenInit: Set pixmap depths failed\n");
	return FALSE;
    }

    height = pScrn->virtualY;
    width = pScrn->virtualX;

    /* shadowfb */
    if ((devPtr->shadowmem = calloc(1, pScrn->virtualX * pScrn->virtualY *
		pScrn->bitsPerPixel)) == NULL) {
	xf86DrvMsg(scrnIndex, X_ERROR,
	    "DevScreenInit: Allocation of shadow memory failed\n");
	return FALSE;
    }
    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
	"Screen init width %d height %d virtual %d %d\n",
	width, height, pScrn->virtualX, pScrn->virtualY);

    ret = fbScreenInit(pScreen, devPtr->shadowmem, width, height,
	pScrn->xDpi, pScrn->yDpi, pScrn->displayWidth, pScrn->bitsPerPixel);

    if (!ret)
	return FALSE;

    if (pScrn->bitsPerPixel > 8) {
	/* Fixup RGB ordering */
	visual = pScreen->visuals + pScreen->numVisuals;
	while (--visual >= pScreen->visuals) {
	    if ((visual->class | DynamicClass) == DirectColor) {
		visual->offsetRed = pScrn->offset.red;
		visual->offsetGreen = pScrn->offset.green;
		visual->offsetBlue = pScrn->offset.blue;
		visual->redMask = pScrn->mask.red;
		visual->greenMask = pScrn->mask.green;
		visual->blueMask = pScrn->mask.blue;
	    }
	}
    }

    /* must be after RGB ordering fixed */
    if (!fbPictureInit(pScreen, NULL, 0))
	xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
	    "RENDER extension initialisation failed.\n");

    if (devPtr->shadowmem && !IVTVDevShadowInit(pScreen)) {
	xf86DrvMsg(scrnIndex, X_ERROR,
	    "DevScreenInit: Shadow framebuffer initialization failed.\n");
	return FALSE;
    }

    if (pScrn->bitsPerPixel == 24)
	xf86DrvMsg(scrnIndex, X_WARNING,
	    "Rotation might be broken in 24 bpp\n");

    xf86SetBlackWhitePixels(pScreen);
    miInitializeBackingStore(pScreen);
    xf86SetBackingStore(pScreen);

    /* software cursor */
    miDCInitialize(pScreen, xf86GetPointerScreenFuncs());

    /* XXX It would be simpler to use miCreateDefColormap() in all cases. */
    if (!miCreateDefColormap(pScreen))
	return FALSE;

    flags = CMAP_PALETTED_TRUECOLOR;
    if (!xf86HandleColormaps(pScreen, 256, 8, ivtvHWLoadPalette, NULL, flags))
	return FALSE;

    xf86DPMSInit(pScreen, ivtvHWDPMSSet, 0);

    pScreen->SaveScreen = ivtvHWSaveScreen;

    /* Wrap the current CloseScreen function */
    devPtr->CloseScreen = pScreen->CloseScreen;
    pScreen->CloseScreen = IVTVDevCloseScreen;

    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Init Video\n");
    IvtvInitVideo(pScreen);

    /* Allocate buffer for yv12 to hm12 conversion */
    devPtr->xv_buffer = malloc(622080);
    if (!devPtr->xv_buffer) {
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
	    "DevScreenInit: Failed to allocate xv conversion buffer\n");
	return 0;
    }

    TRACE_EXIT("IVTVDevScreenInit");

    return TRUE;
}

static Bool
IVTVDevCloseScreen(int scrnIndex, ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
    IVTVDevPtr devPtr = IVTVDEVPTR(pScrn);

    /* Before we go, wipe the screen. If there is no console
     * running on the framebuffer, this removes the garbage */
    memset(devPtr->shadowmem, 0,
	devPtr->shadow_width * devPtr->shadow_height * (pScrn->bitsPerPixel / 8));
    ivtvHWSendDMA(pScrn, devPtr->shadowmem, 0, devPtr->shadow_width, 0,
	devPtr->shadow_height);

#ifdef IVTVDEVHW_BYTE_SWAP
    ivtvHWUnmapVidmem(pScrn);
#endif
    ivtvHWRestore(pScrn);
    free(devPtr->shadowmem);
    pScrn->vtSema = FALSE;

    if (devPtr->xv_buffer) {
	free(devPtr->xv_buffer);
	devPtr->xv_buffer = 0;
    }

    pScreen->CloseScreen = devPtr->CloseScreen;
    return (*pScreen->CloseScreen) (scrnIndex, pScreen);
}
