/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/via/via_dri.c,v 1.4 2003/09/24 02:43:30 dawes Exp $ */
/*
 * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
 * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved.
 *
 * 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, sub license,
 * 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 (including the
 * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL
 * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS 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.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
 
#include "driver.h"
#include "drm.h"
#include "imports.h"

#include "dri_util.h"

#include "via_context.h"
#include "via_dri.h"
#include "via_driver.h"
#include "via_common.h"
#include "xf86drm.h"

static void VIAEnableMMIO(DRIDriverContext * ctx);
static void VIADisableMMIO(DRIDriverContext * ctx);
static void VIADisableExtendedFIFO(DRIDriverContext *ctx);
static void VIAEnableExtendedFIFO(DRIDriverContext *ctx);
static void VIAInitialize2DEngine(DRIDriverContext *ctx);
static void VIAInitialize3DEngine(DRIDriverContext *ctx);

static int VIADRIScreenInit(DRIDriverContext * ctx);
static void VIADRICloseScreen(DRIDriverContext * ctx);
static int VIADRIFinishScreenInit(DRIDriverContext * ctx);

/* _SOLO : missing macros normally defined by X code */
#define xf86DrvMsg(a, b, ...) fprintf(stderr, __VA_ARGS__)
#define MMIO_IN8(base, addr) ((*(((volatile u_int8_t*)base)+(addr)))+0)
#define MMIO_OUT8(base, addr, val) ((*(((volatile u_int8_t*)base)+(addr)))=((u_int8_t)val))
#define MMIO_OUT16(base, addr, val) ((*(volatile u_int16_t*)(((u_int8_t*)base)+(addr)))=((u_int16_t)val))

#define VIDEO	0 
#define AGP		1
#define AGP_PAGE_SIZE 4096
#define AGP_PAGES 8192
#define AGP_SIZE (AGP_PAGE_SIZE * AGP_PAGES)
#define AGP_CMDBUF_PAGES 512
#define AGP_CMDBUF_SIZE (AGP_PAGE_SIZE * AGP_CMDBUF_PAGES)

static char VIAKernelDriverName[] = "via";
static char VIAClientDriverName[] = "unichrome";

static int VIADRIAgpInit(const DRIDriverContext *ctx, VIAPtr pVia);
static int VIADRIPciInit(DRIDriverContext * ctx, VIAPtr pVia);
static int VIADRIFBInit(DRIDriverContext * ctx, VIAPtr pVia);
static int VIADRIKernelInit(DRIDriverContext * ctx, VIAPtr pVia);
static int VIADRIMapInit(DRIDriverContext * ctx, VIAPtr pVia);

static void VIADRIIrqInit( DRIDriverContext *ctx )
{
    VIAPtr pVia = VIAPTR(ctx);
    VIADRIPtr pVIADRI = pVia->devPrivate;

    pVIADRI->irqEnabled = drmGetInterruptFromBusID(pVia->drmFD,
					   ctx->pciBus,
					   ctx->pciDevice,
					   ctx->pciFunc);

    if ((drmCtlInstHandler(pVia->drmFD, pVIADRI->irqEnabled))) {
	xf86DrvMsg(pScreen->myNum, X_WARNING,
		   "[drm] Failure adding irq handler. "
		   "Falling back to irq-free operation.\n");
	pVIADRI->irqEnabled = 0;
    }

    if (pVIADRI->irqEnabled)
	xf86DrvMsg(pScreen->myNum, X_INFO,
		   "[drm] Irq handler installed, using IRQ %d.\n",
		   pVIADRI->irqEnabled);
}

static void VIADRIIrqExit( DRIDriverContext *ctx ) {
    VIAPtr pVia = VIAPTR(ctx);
    VIADRIPtr pVIADRI = pVia->devPrivate;

    if (pVIADRI->irqEnabled) {
	if (drmCtlUninstHandler(pVia->drmFD)) {
	    xf86DrvMsg(pScreen-myNum, X_INFO,"[drm] Irq handler uninstalled.\n");
	} else {
	    xf86DrvMsg(pScreen->myNum, X_ERROR,
		       "[drm] Could not uninstall irq handler.\n");
	}
    }
}
	    
static void VIADRIRingBufferCleanup(DRIDriverContext *ctx)
{
    VIAPtr pVia = VIAPTR(ctx);
    VIADRIPtr pVIADRI = pVia->devPrivate;
    drm_via_dma_init_t ringBufInit;

    if (pVIADRI->ringBufActive) {
	xf86DrvMsg(pScreen->myNum, X_INFO, 
		   "[drm] Cleaning up DMA ring-buffer.\n");
	ringBufInit.func = VIA_CLEANUP_DMA;
	if (drmCommandWrite(pVia->drmFD, DRM_VIA_DMA_INIT, &ringBufInit,
			    sizeof(ringBufInit))) {
	    xf86DrvMsg(pScreen->myNum, X_WARNING, 
		       "[drm] Failed to clean up DMA ring-buffer: %d\n", errno);
	}
	pVIADRI->ringBufActive = 0;
    }
}

static int VIADRIRingBufferInit(DRIDriverContext *ctx)
{
    VIAPtr pVia = VIAPTR(ctx);
    VIADRIPtr pVIADRI = pVia->devPrivate;
    drm_via_dma_init_t ringBufInit;
    drmVersionPtr drmVer;

    pVIADRI->ringBufActive = 0;

    if (NULL == (drmVer = drmGetVersion(pVia->drmFD))) {
	return GL_FALSE;
    }

    if (((drmVer->version_major <= 1) && (drmVer->version_minor <= 3))) {
	return GL_FALSE;
    } 

    /*
     * Info frome code-snippet on DRI-DEVEL list; Erdi Chen.
     */

    switch (pVia->ChipId) {
    case PCI_CHIP_VT3259:
    	ringBufInit.reg_pause_addr = 0x40c;
	break;
    default:
    	ringBufInit.reg_pause_addr = 0x418;
	break;
    }
   
    ringBufInit.offset = pVia->agpSize;
    ringBufInit.size = AGP_CMDBUF_SIZE;
    ringBufInit.func = VIA_INIT_DMA;
    if (drmCommandWrite(pVia->drmFD, DRM_VIA_DMA_INIT, &ringBufInit,
			sizeof(ringBufInit))) {
	xf86DrvMsg(pScreen->myNum, X_ERROR, 
		   "[drm] Failed to initialize DMA ring-buffer: %d\n", errno);
	return GL_FALSE;
    }
    xf86DrvMsg(pScreen->myNum, X_INFO, 
	       "[drm] Initialized AGP ring-buffer, size 0x%lx at AGP offset 0x%lx.\n",
	       ringBufInit.size, ringBufInit.offset);
   
    pVIADRI->ringBufActive = 1;
    return GL_TRUE;
}	    

static int VIADRIAgpInit(const DRIDriverContext *ctx, VIAPtr pVia)
{
    unsigned long  agp_phys;
    drmAddress agpaddr;
    VIADRIPtr pVIADRI;
    pVIADRI = pVia->devPrivate;
    pVia->agpSize = 0;

    if (drmAgpAcquire(pVia->drmFD) < 0) {
        xf86DrvMsg(pScreen->myNum, X_ERROR, "[drm] drmAgpAcquire failed %d\n", errno);
        return GL_FALSE;
    }

    if (drmAgpEnable(pVia->drmFD, drmAgpGetMode(pVia->drmFD)&~0x0) < 0) {
         xf86DrvMsg(pScreen->myNum, X_ERROR, "[drm] drmAgpEnable failed\n");
        return GL_FALSE;
    }
    
    xf86DrvMsg(pScreen->myNum, X_INFO, "[drm] drmAgpEnabled succeeded\n");

    if (drmAgpAlloc(pVia->drmFD, AGP_SIZE, 0, &agp_phys, &pVia->agpHandle) < 0) {
        xf86DrvMsg(pScreen->myNum, X_ERROR,
                 "[drm] drmAgpAlloc failed\n");
        drmAgpRelease(pVia->drmFD);
        return GL_FALSE;
    }
   
    if (drmAgpBind(pVia->drmFD, pVia->agpHandle, 0) < 0) {
        xf86DrvMsg(pScreen->myNum, X_ERROR,
                 "[drm] drmAgpBind failed\n");
        drmAgpFree(pVia->drmFD, pVia->agpHandle);
        drmAgpRelease(pVia->drmFD);

        return GL_FALSE;
    }

    /*
     * Place the ring-buffer last in the AGP region, and restrict the
     * public map not to include the buffer for security reasons.
     */

    pVia->agpSize = AGP_SIZE - AGP_CMDBUF_SIZE;
    pVia->agpAddr = drmAgpBase(pVia->drmFD);
    xf86DrvMsg(pScreen->myNum, X_INFO,
                 "[drm] agpAddr = 0x%08lx\n",pVia->agpAddr);
		 
    pVIADRI->agp.size = pVia->agpSize;
    if (drmAddMap(pVia->drmFD, (drm_handle_t)0,
                 pVIADRI->agp.size, DRM_AGP, 0, 
                 &pVIADRI->agp.handle) < 0) {
	xf86DrvMsg(pScreen->myNum, X_ERROR,
	    "[drm] Failed to map public agp area\n");
        pVIADRI->agp.size = 0;
        return GL_FALSE;
    }  
    /* Map AGP from kernel to Xserver - Not really needed */
    drmMap(pVia->drmFD, pVIADRI->agp.handle,pVIADRI->agp.size, &agpaddr);

    xf86DrvMsg(pScreen->myNum, X_INFO, 
                "[drm] agpAddr = 0x%08lx\n", pVia->agpAddr);
    xf86DrvMsg(pScreen->myNum, X_INFO, 
                "[drm] agpSize = 0x%08lx\n", pVia->agpSize);
    xf86DrvMsg(pScreen->myNum, X_INFO, 
                "[drm] agp physical addr = 0x%08lx\n", agp_phys);

    {
	drm_via_agp_t agp;
	agp.offset = 0;
	agp.size = AGP_SIZE-AGP_CMDBUF_SIZE;
	if (drmCommandWrite(pVia->drmFD, DRM_VIA_AGP_INIT, &agp,
			    sizeof(drm_via_agp_t)) < 0) {
	    drmUnmap(&agpaddr,pVia->agpSize);
	    drmRmMap(pVia->drmFD,pVIADRI->agp.handle);
	    drmAgpUnbind(pVia->drmFD, pVia->agpHandle);
	    drmAgpFree(pVia->drmFD, pVia->agpHandle);
	    drmAgpRelease(pVia->drmFD);
	    return GL_FALSE;
	}
    }

    return GL_TRUE;
}

static int VIADRIFBInit(DRIDriverContext * ctx, VIAPtr pVia)
{   
    int FBSize = pVia->FBFreeEnd-pVia->FBFreeStart;
    int FBOffset = pVia->FBFreeStart; 
    VIADRIPtr pVIADRI = pVia->devPrivate;
    pVIADRI->fbOffset = FBOffset;
    pVIADRI->fbSize = pVia->videoRambytes;

    {
	drm_via_fb_t fb;
	fb.offset = FBOffset;
	fb.size = FBSize;
	
	if (drmCommandWrite(pVia->drmFD, DRM_VIA_FB_INIT, &fb,
			    sizeof(drm_via_fb_t)) < 0) {
	    xf86DrvMsg(pScreen->myNum, X_ERROR,
		       "[drm] failed to init frame buffer area\n");
	    return GL_FALSE;
	} else {
	    xf86DrvMsg(pScreen->myNum, X_INFO,
		       "[drm] FBFreeStart= 0x%08x FBFreeEnd= 0x%08x "
		       "FBSize= 0x%08x\n",
		       pVia->FBFreeStart, pVia->FBFreeEnd, FBSize);
	    return GL_TRUE;	
	}   
    }
}

static int VIADRIPciInit(DRIDriverContext * ctx, VIAPtr pVia)
{
    return GL_TRUE;	
}

static int VIADRIScreenInit(DRIDriverContext * ctx)
{
    VIAPtr pVia = VIAPTR(ctx);
    VIADRIPtr pVIADRI;
    int err;

#if 0
    ctx->shared.SAREASize = ((sizeof(drm_sarea_t) + 0xfff) & 0x1000);
#else
    if (sizeof(drm_sarea_t)+sizeof(drm_via_sarea_t) > SAREA_MAX) {
	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
			"Data does not fit in SAREA\n");
	return GL_FALSE;
    }
    ctx->shared.SAREASize = SAREA_MAX;
#endif

    ctx->drmFD = drmOpen(VIAKernelDriverName, NULL);
    if (ctx->drmFD < 0) {
        fprintf(stderr, "[drm] drmOpen failed\n");
        return 0;
    }
    pVia->drmFD = ctx->drmFD;

    err = drmSetBusid(ctx->drmFD, ctx->pciBusID);
    if (err < 0) {
        fprintf(stderr, "[drm] drmSetBusid failed (%d, %s), %s\n",
                ctx->drmFD, ctx->pciBusID, strerror(-err));
        return 0;
    }

    err = drmAddMap(ctx->drmFD, 0, ctx->shared.SAREASize, DRM_SHM,
                  DRM_CONTAINS_LOCK, &ctx->shared.hSAREA);
    if (err < 0) {
        fprintf(stderr, "[drm] drmAddMap failed\n");
        return 0;
    }
    fprintf(stderr, "[drm] added %d byte SAREA at 0x%08lx\n",
            ctx->shared.SAREASize, ctx->shared.hSAREA);

    if (drmMap(ctx->drmFD,
               ctx->shared.hSAREA,
               ctx->shared.SAREASize,
               (drmAddressPtr)(&ctx->pSAREA)) < 0)
    {
        fprintf(stderr, "[drm] drmMap failed\n");
        return 0;
    }
    memset(ctx->pSAREA, 0, ctx->shared.SAREASize);
    fprintf(stderr, "[drm] mapped SAREA 0x%08lx to %p, size %d\n",
            ctx->shared.hSAREA, ctx->pSAREA, ctx->shared.SAREASize);

    /* Need to AddMap the framebuffer and mmio regions here:
     */
    if (drmAddMap(ctx->drmFD,
                  (drm_handle_t)ctx->FBStart,
                  ctx->FBSize,
                  DRM_FRAME_BUFFER,
#ifndef _EMBEDDED
                   0,
#else
                   DRM_READ_ONLY,
#endif
                   &ctx->shared.hFrameBuffer) < 0)
    {
        fprintf(stderr, "[drm] drmAddMap framebuffer failed\n");
        return 0;
    }

    fprintf(stderr, "[drm] framebuffer handle = 0x%08lx\n",
            ctx->shared.hFrameBuffer);

    pVIADRI = (VIADRIPtr) CALLOC(sizeof(VIADRIRec));
    if (!pVIADRI) {
        drmClose(ctx->drmFD);
        return GL_FALSE;
    }
    pVia->devPrivate = pVIADRI;
    ctx->driverClientMsg = pVIADRI;
    ctx->driverClientMsgSize = sizeof(*pVIADRI);

    /* DRIScreenInit doesn't add all the common mappings.  Add additional mappings here. */
    if (!VIADRIMapInit(ctx, pVia)) {
	VIADRICloseScreen(ctx);
	return GL_FALSE;
    }

    pVIADRI->regs.size = VIA_MMIO_REGSIZE;
    pVIADRI->regs.handle = pVia->registerHandle;
    xf86DrvMsg(pScreen->myNum, X_INFO, "[drm] mmio Registers = 0x%08lx\n",
	pVIADRI->regs.handle);

    if (drmMap(pVia->drmFD,
               pVIADRI->regs.handle,
               pVIADRI->regs.size,
               (drmAddress *)&pVia->MapBase) != 0)
    {
        VIADRICloseScreen(ctx);
        return GL_FALSE;
    }

    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "[dri] mmio mapped.\n" );

    VIAEnableMMIO(ctx);

    /* Get video memory clock. */
    VGAOUT8(0x3D4, 0x3D);
    pVia->MemClk = (VGAIN8(0x3D5) & 0xF0) >> 4;
    xf86DrvMsg(0, X_INFO, "[dri] MemClk (0x%x)\n", pVia->MemClk);

    /* 3D rendering has noise if not enabled. */
    VIAEnableExtendedFIFO(ctx);

    VIAInitialize2DEngine(ctx);

    /* Must disable MMIO or 3D won't work. */
    VIADisableMMIO(ctx);

    VIAInitialize3DEngine(ctx);

    pVia->IsPCI = !VIADRIAgpInit(ctx, pVia);

    if (pVia->IsPCI) {
        VIADRIPciInit(ctx, pVia);
	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "[dri] use pci.\n" );
    }
    else
        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "[dri] use agp.\n" );

    if (!(VIADRIFBInit(ctx, pVia))) {
	VIADRICloseScreen(ctx);
        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "[dri] frame buffer initialize fail .\n" );
        return GL_FALSE;
    }
    
    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "[dri] frame buffer initialized.\n" );
 
    return VIADRIFinishScreenInit(ctx);
}

static void
VIADRICloseScreen(DRIDriverContext * ctx)
{
    VIAPtr pVia = VIAPTR(ctx);
    VIADRIPtr pVIADRI=(VIADRIPtr)pVia->devPrivate;

    VIADRIRingBufferCleanup(ctx);

    if (pVia->MapBase) {
	xf86DrvMsg(pScreen->myNum, X_INFO, "[drm] Unmapping MMIO registers\n");
        drmUnmap(pVia->MapBase, pVIADRI->regs.size);
    }

    if (pVia->agpSize) {
	xf86DrvMsg(pScreen->myNum, X_INFO, "[drm] Freeing agp memory\n");
        drmAgpFree(pVia->drmFD, pVia->agpHandle);
	xf86DrvMsg(pScreen->myNum, X_INFO, "[drm] Releasing agp module\n");
    	drmAgpRelease(pVia->drmFD);
    }

#if 0
    if (pVia->DRIIrqEnable) 
#endif
        VIADRIIrqExit(ctx);
}

static int
VIADRIFinishScreenInit(DRIDriverContext * ctx)
{
    VIAPtr pVia = VIAPTR(ctx);
    VIADRIPtr pVIADRI;
    int err;

    err = drmCreateContext(ctx->drmFD, &ctx->serverContext);
    if (err != 0) {
        fprintf(stderr, "%s: drmCreateContext failed %d\n", __FUNCTION__, err);
        return GL_FALSE;
    }

    DRM_LOCK(ctx->drmFD, ctx->pSAREA, ctx->serverContext, 0);


    if (!VIADRIKernelInit(ctx, pVia)) {
	VIADRICloseScreen(ctx);
	return GL_FALSE;
    }
    xf86DrvMsg(pScreen->myNum, X_INFO, "[dri] kernel data initialized.\n");

    /* set SAREA value */
    {
	drm_via_sarea_t *saPriv;

	saPriv=(drm_via_sarea_t*)(((char*)ctx->pSAREA) +
                               sizeof(drm_sarea_t));
	assert(saPriv);
	memset(saPriv, 0, sizeof(*saPriv));
	saPriv->ctxOwner = -1;
    }
    pVIADRI=(VIADRIPtr)pVia->devPrivate;
    pVIADRI->deviceID=pVia->Chipset;  
    pVIADRI->width=ctx->shared.virtualWidth;
    pVIADRI->height=ctx->shared.virtualHeight;
    pVIADRI->mem=ctx->shared.fbSize;
    pVIADRI->bytesPerPixel= (ctx->bpp+7) / 8; 
    pVIADRI->sarea_priv_offset = sizeof(drm_sarea_t);
    /* TODO */
    pVIADRI->scrnX=pVIADRI->width;
    pVIADRI->scrnY=pVIADRI->height;

    /* Initialize IRQ */
#if 0
    if (pVia->DRIIrqEnable) 
#endif
	VIADRIIrqInit(ctx);
    
    pVIADRI->ringBufActive = 0;
    VIADRIRingBufferInit(ctx);

    return GL_TRUE;
}

/* Initialize the kernel data structures. */
static int VIADRIKernelInit(DRIDriverContext * ctx, VIAPtr pVia)
{
    drm_via_init_t drmInfo;
    memset(&drmInfo, 0, sizeof(drm_via_init_t));
    drmInfo.sarea_priv_offset   = sizeof(drm_sarea_t);
    drmInfo.func = VIA_INIT_MAP;
    drmInfo.fb_offset           = pVia->FrameBufferBase;
    drmInfo.mmio_offset         = pVia->registerHandle;
    if (pVia->IsPCI)
	drmInfo.agpAddr = (u_int32_t)NULL;
    else
	drmInfo.agpAddr = (u_int32_t)pVia->agpAddr;

    if ((drmCommandWrite(pVia->drmFD, DRM_VIA_MAP_INIT,&drmInfo,
			     sizeof(drm_via_init_t))) < 0)
	    return GL_FALSE;

    return GL_TRUE;
}
/* Add a map for the MMIO registers */
static int VIADRIMapInit(DRIDriverContext * ctx, VIAPtr pVia)
{
    int flags = 0;

    if (drmAddMap(pVia->drmFD, pVia->MmioBase, VIA_MMIO_REGSIZE,
		  DRM_REGISTERS, flags, &pVia->registerHandle) < 0) {
	return GL_FALSE;
    }

    xf86DrvMsg(pScreen->myNum, X_INFO,
	"[drm] register handle = 0x%08lx\n", pVia->registerHandle);

    return GL_TRUE;
}

static int viaValidateMode(const DRIDriverContext *ctx)
{
    VIAPtr pVia = VIAPTR(ctx);

    return 1;
}

static int viaPostValidateMode(const DRIDriverContext *ctx)
{
    VIAPtr pVia = VIAPTR(ctx);

    return 1;
}

static void VIAEnableMMIO(DRIDriverContext * ctx)
{
    /*vgaHWPtr hwp = VGAHWPTR(ctx);*/
    VIAPtr pVia = VIAPTR(ctx);
    unsigned char val;

#if 0
    if (xf86IsPrimaryPci(pVia->PciInfo)) {
        /* If we are primary card, we still use std vga port. If we use
         * MMIO, system will hang in vgaHWSave when our card used in
         * PLE and KLE (integrated Trident MVP4)
         */
        vgaHWSetStdFuncs(hwp);
    }
    else {
        vgaHWSetMmioFuncs(hwp, pVia->MapBase, 0x8000);
    }
#endif

    val = VGAIN8(0x3c3);
    VGAOUT8(0x3c3, val | 0x01);
    val = VGAIN8(0x3cc);
    VGAOUT8(0x3c2, val | 0x01);

    /* Unlock Extended IO Space */
    VGAOUT8(0x3c4, 0x10);
    VGAOUT8(0x3c5, 0x01);

    /* Enable MMIO */
    if(!pVia->IsSecondary) {
	VGAOUT8(0x3c4, 0x1a);
	val = VGAIN8(0x3c5);
#ifdef DEBUG
	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "primary val = %x\n", val);
#endif
	VGAOUT8(0x3c5, val | 0x68);
    }
    else {
	VGAOUT8(0x3c4, 0x1a);
	val = VGAIN8(0x3c5);
#ifdef DEBUG
	xf86DrvMsg(pScrn->scrnIndex, X_INFO, "secondary val = %x\n", val);
#endif
	VGAOUT8(0x3c5, val | 0x38);
    }

    /* Unlock CRTC registers */
    VGAOUT8(0x3d4, 0x47);
    VGAOUT8(0x3d5, 0x00);

    return;
}

static void VIADisableMMIO(DRIDriverContext * ctx)
{
    VIAPtr pVia = VIAPTR(ctx);
    unsigned char val;

    VGAOUT8(0x3c4, 0x1a);
    val = VGAIN8(0x3c5);
    VGAOUT8(0x3c5, val & 0x97);

    return;
}

static void VIADisableExtendedFIFO(DRIDriverContext *ctx)
{
    VIAPtr  pVia = VIAPTR(ctx);
    u_int32_t  dwGE230, dwGE298;

    /* Cause of exit XWindow will dump back register value, others chipset no
     * need to set extended fifo value */
    if (pVia->Chipset == VIA_CLE266 && pVia->ChipRev < 15 &&
        (ctx->shared.virtualWidth > 1024 || pVia->HasSecondary)) {
        /* Turn off Extend FIFO */
        /* 0x298[29] */
        dwGE298 = VIAGETREG(0x298);
        VIASETREG(0x298, dwGE298 | 0x20000000);
        /* 0x230[21] */
        dwGE230 = VIAGETREG(0x230);
        VIASETREG(0x230, dwGE230 & ~0x00200000);
        /* 0x298[29] */
        dwGE298 = VIAGETREG(0x298);
        VIASETREG(0x298, dwGE298 & ~0x20000000);
    }
}

static void VIAEnableExtendedFIFO(DRIDriverContext *ctx)
{
    VIAPtr  pVia = VIAPTR(ctx);
    u_int8_t   bRegTemp;
    u_int32_t  dwGE230, dwGE298;

    switch (pVia->Chipset) {
    case VIA_CLE266:
        if (pVia->ChipRev > 14) {  /* For 3123Cx */
            if (pVia->HasSecondary) {  /* SAMM or DuoView case */
                if (ctx->shared.virtualWidth >= 1024)
    	        {
    	            /* 3c5.16[0:5] */
        	        VGAOUT8(0x3C4, 0x16);
            	    bRegTemp = VGAIN8(0x3C5);
    	            bRegTemp &= ~0x3F;
        	        bRegTemp |= 0x1C;
            	    VGAOUT8(0x3C5, bRegTemp);
        	        /* 3c5.17[0:6] */
            	    VGAOUT8(0x3C4, 0x17);
                	bRegTemp = VGAIN8(0x3C5);
    	            bRegTemp &= ~0x7F;
        	        bRegTemp |= 0x3F;
            	    VGAOUT8(0x3C5, bRegTemp);
            	    pVia->EnableExtendedFIFO = GL_TRUE;
    	        }
            }
            else   /* Single view or Simultaneoue case */
            {
                if (ctx->shared.virtualWidth > 1024)
    	        {
    	            /* 3c5.16[0:5] */
        	        VGAOUT8(0x3C4, 0x16);
            	    bRegTemp = VGAIN8(0x3C5);
    	            bRegTemp &= ~0x3F;
        	        bRegTemp |= 0x17;
            	    VGAOUT8(0x3C5, bRegTemp);
        	        /* 3c5.17[0:6] */
            	    VGAOUT8(0x3C4, 0x17);
                	bRegTemp = VGAIN8(0x3C5);
    	            bRegTemp &= ~0x7F;
        	        bRegTemp |= 0x2F;
            	    VGAOUT8(0x3C5, bRegTemp);
            	    pVia->EnableExtendedFIFO = GL_TRUE;
    	        }
            }
            /* 3c5.18[0:5] */
            VGAOUT8(0x3C4, 0x18);
            bRegTemp = VGAIN8(0x3C5);
            bRegTemp &= ~0x3F;
            bRegTemp |= 0x17;
            bRegTemp |= 0x40;  /* force the preq always higher than treq */
            VGAOUT8(0x3C5, bRegTemp);
        }
        else {      /* for 3123Ax */
            if (ctx->shared.virtualWidth > 1024 || pVia->HasSecondary) {
                /* Turn on Extend FIFO */
                /* 0x298[29] */
                dwGE298 = VIAGETREG(0x298);
                VIASETREG(0x298, dwGE298 | 0x20000000);
                /* 0x230[21] */
                dwGE230 = VIAGETREG(0x230);
                VIASETREG(0x230, dwGE230 | 0x00200000);
                /* 0x298[29] */
                dwGE298 = VIAGETREG(0x298);
                VIASETREG(0x298, dwGE298 & ~0x20000000);

                /* 3c5.16[0:5] */
                VGAOUT8(0x3C4, 0x16);
                bRegTemp = VGAIN8(0x3C5);
                bRegTemp &= ~0x3F;
                bRegTemp |= 0x17;
                /* bRegTemp |= 0x10; */
                VGAOUT8(0x3C5, bRegTemp);
                /* 3c5.17[0:6] */
                VGAOUT8(0x3C4, 0x17);
                bRegTemp = VGAIN8(0x3C5);
                bRegTemp &= ~0x7F;
                bRegTemp |= 0x2F;
                /*bRegTemp |= 0x1F;*/
                VGAOUT8(0x3C5, bRegTemp);
                /* 3c5.18[0:5] */
                VGAOUT8(0x3C4, 0x18);
                bRegTemp = VGAIN8(0x3C5);
                bRegTemp &= ~0x3F;
                bRegTemp |= 0x17;
                bRegTemp |= 0x40;  /* force the preq always higher than treq */
                VGAOUT8(0x3C5, bRegTemp);
          	    pVia->EnableExtendedFIFO = GL_TRUE;
            }
        }
        break;
    case VIA_KM400:
        if (pVia->HasSecondary) {  /* SAMM or DuoView case */
            if ((ctx->shared.virtualWidth >= 1600) &&
                (pVia->MemClk <= VIA_MEM_DDR200)) {
        	    /* enable CRT extendded FIFO */
            	VGAOUT8(0x3C4, 0x17);
                VGAOUT8(0x3C5, 0x1C);
    	        /* revise second display queue depth and read threshold */
        	    VGAOUT8(0x3C4, 0x16);
            	bRegTemp = VGAIN8(0x3C5);
    	        bRegTemp &= ~0x3F;
    	        bRegTemp = (bRegTemp) | (0x09);
                VGAOUT8(0x3C5, bRegTemp);
            }
            else {
                /* enable CRT extendded FIFO */
                VGAOUT8(0x3C4, 0x17);
                VGAOUT8(0x3C5,0x3F);
                /* revise second display queue depth and read threshold */
                VGAOUT8(0x3C4, 0x16);
                bRegTemp = VGAIN8(0x3C5);
                bRegTemp &= ~0x3F;
                bRegTemp = (bRegTemp) | (0x1C);
                VGAOUT8(0x3C5, bRegTemp);
            }
            /* 3c5.18[0:5] */
            VGAOUT8(0x3C4, 0x18);
            bRegTemp = VGAIN8(0x3C5);
            bRegTemp &= ~0x3F;
            bRegTemp |= 0x17;
            bRegTemp |= 0x40;  /* force the preq always higher than treq */
            VGAOUT8(0x3C5, bRegTemp);
       	    pVia->EnableExtendedFIFO = GL_TRUE;
        }
        else {
            if ( (ctx->shared.virtualWidth > 1024) && (ctx->shared.virtualWidth <= 1280) )
            {
                /* enable CRT extendded FIFO */
                VGAOUT8(0x3C4, 0x17);
                VGAOUT8(0x3C5, 0x3F);
                /* revise second display queue depth and read threshold */
                VGAOUT8(0x3C4, 0x16);
                bRegTemp = VGAIN8(0x3C5);
                bRegTemp &= ~0x3F;
                bRegTemp = (bRegTemp) | (0x17);
                VGAOUT8(0x3C5, bRegTemp);
           	    pVia->EnableExtendedFIFO = GL_TRUE;
            }
            else if ((ctx->shared.virtualWidth > 1280))
            {
                /* enable CRT extendded FIFO */
                VGAOUT8(0x3C4, 0x17);
                VGAOUT8(0x3C5, 0x3F);
                /* revise second display queue depth and read threshold */
                VGAOUT8(0x3C4, 0x16);
                bRegTemp = VGAIN8(0x3C5);
                bRegTemp &= ~0x3F;
                bRegTemp = (bRegTemp) | (0x1C);
                VGAOUT8(0x3C5, bRegTemp);
           	    pVia->EnableExtendedFIFO = GL_TRUE;
            }
            else
            {
                /* enable CRT extendded FIFO */
                VGAOUT8(0x3C4, 0x17);
                VGAOUT8(0x3C5, 0x3F);
                /* revise second display queue depth and read threshold */
                VGAOUT8(0x3C4, 0x16);
                bRegTemp = VGAIN8(0x3C5);
                bRegTemp &= ~0x3F;
                bRegTemp = (bRegTemp) | (0x10);
                VGAOUT8(0x3C5, bRegTemp);
            }
            /* 3c5.18[0:5] */
            VGAOUT8(0x3C4, 0x18);
            bRegTemp = VGAIN8(0x3C5);
            bRegTemp &= ~0x3F;
            bRegTemp |= 0x17;
            bRegTemp |= 0x40;  /* force the preq always higher than treq */
            VGAOUT8(0x3C5, bRegTemp);
        }
        break;
    case VIA_K8M800:
        /*=* R1 Display FIFO depth (384 /8 -1 -> 0xbf) SR17[7:0] (8bits) *=*/
        VGAOUT8(0x3c4, 0x17);
        VGAOUT8(0x3c5, 0xbf);

        /*=* R2 Display fetch datum threshold value (328/4 -> 0x52)
             SR16[5:0], SR16[7] (7bits) *=*/
        VGAOUT8(0x3c4, 0x16);
        bRegTemp = VGAIN8(0x3c5) & ~0xBF;
        bRegTemp |= (0x52 & 0x3F);
        bRegTemp |= ((0x52 & 0x40) << 1);
        VGAOUT8(0x3c5, bRegTemp);

        /*=* R3 Switch to the highest agent threshold value (74 -> 0x4a)
             SR18[5:0], SR18[7] (7bits) *=*/
        VGAOUT8(0x3c4, 0x18);
        bRegTemp = VGAIN8(0x3c5) & ~0xBF;
        bRegTemp |= (0x4a & 0x3F);
        bRegTemp |= ((0x4a & 0x40) << 1);
        VGAOUT8(0x3c5, bRegTemp);
#if 0
        /*=* R4 Fetch Number for a scan line (unit: 8 bytes)
             SR1C[7:0], SR1D[1:0] (10bits) *=*/
        wRegTemp = (pBIOSInfo->offsetWidthByQWord >> 1) + 4;
        VGAOUT8(0x3c4, 0x1c);
        VGAOUT8(0x3c5, (u_int8_t)(wRegTemp & 0xFF));
        VGAOUT8(0x3c4, 0x1d);
        bRegTemp = VGAIN8(0x3c5) & ~0x03;
        VGAOUT8(0x3c5, bRegTemp | ((wRegTemp & 0x300) >> 8));
#endif
        if (ctx->shared.virtualWidth >= 1400 && ctx->bpp == 32)
        {
            /*=* Max. length for a request SR22[4:0] (64/4 -> 0x10) *=*/
            VGAOUT8(0x3c4, 0x22);
            bRegTemp = VGAIN8(0x3c5) & ~0x1F;
            VGAOUT8(0x3c5, bRegTemp | 0x10);
        }
        else
        {
            /*=* Max. length for a request SR22[4:0]
                 (128/4 -> over flow 0x0) *=*/
            VGAOUT8(0x3c4, 0x22);
            bRegTemp = VGAIN8(0x3c5) & ~0x1F;
            VGAOUT8(0x3c5, bRegTemp);
        }
        break;
    case VIA_PM800:
        /*=* R1 Display FIFO depth (96-1 -> 0x5f) SR17[7:0] (8bits) *=*/
        VGAOUT8(0x3c4, 0x17);
        VGAOUT8(0x3c5, 0x5f);

        /*=* R2 Display fetch datum threshold value (32 -> 0x20)
             SR16[5:0], SR16[7] (7bits) *=*/
        VGAOUT8(0x3c4, 0x16);
        bRegTemp = VGAIN8(0x3c5) & ~0xBF;
        bRegTemp |= (0x20 & 0x3F);
        bRegTemp |= ((0x20 & 0x40) << 1);
        VGAOUT8(0x3c5, bRegTemp);

        /*=* R3 Switch to the highest agent threshold value (16 -> 0x10)
             SR18[5:0], SR18[7] (7bits) *=*/
        VGAOUT8(0x3c4, 0x18);
        bRegTemp = VGAIN8(0x3c5) & ~0xBF;
        bRegTemp |= (0x10 & 0x3F);
        bRegTemp |= ((0x10 & 0x40) << 1);
        VGAOUT8(0x3c5, bRegTemp);
#if 0
        /*=* R4 Fetch Number for a scan line (unit: 8 bytes)
             SR1C[7:0], SR1D[1:0] (10bits) *=*/
        wRegTemp = (pBIOSInfo->offsetWidthByQWord >> 1) + 4;
        VGAOUT8(0x3c4, 0x1c);
        VGAOUT8(0x3c5, (u_int8_t)(wRegTemp & 0xFF));
        VGAOUT8(0x3c4, 0x1d);
        bRegTemp = VGAIN8(0x3c5) & ~0x03;
        VGAOUT8(0x3c5, bRegTemp | ((wRegTemp & 0x300) >> 8));
#endif
        if (ctx->shared.virtualWidth >= 1400 && ctx->bpp == 32)
        {
            /*=* Max. length for a request SR22[4:0] (64/4 -> 0x10) *=*/
            VGAOUT8(0x3c4, 0x22);
            bRegTemp = VGAIN8(0x3c5) & ~0x1F;
            VGAOUT8(0x3c5, bRegTemp | 0x10);
        }
        else
        {
            /*=* Max. length for a request SR22[4:0] (0x1F) *=*/
            VGAOUT8(0x3c4, 0x22);
            bRegTemp = VGAIN8(0x3c5) & ~0x1F;
            VGAOUT8(0x3c5, bRegTemp | 0x1F);
        }
        break;
    default:
        break;
    }
}

static void VIAInitialize2DEngine(DRIDriverContext *ctx)
{
    VIAPtr  pVia = VIAPTR(ctx);
    u_int32_t  dwVQStartAddr, dwVQEndAddr;
    u_int32_t  dwVQLen, dwVQStartL, dwVQEndL, dwVQStartEndH;
    u_int32_t  dwGEMode;

    /* init 2D engine regs to reset 2D engine */
    VIASETREG(0x04, 0x0);
    VIASETREG(0x08, 0x0);
    VIASETREG(0x0c, 0x0);
    VIASETREG(0x10, 0x0);
    VIASETREG(0x14, 0x0);
    VIASETREG(0x18, 0x0);
    VIASETREG(0x1c, 0x0);
    VIASETREG(0x20, 0x0);
    VIASETREG(0x24, 0x0);
    VIASETREG(0x28, 0x0);
    VIASETREG(0x2c, 0x0);
    VIASETREG(0x30, 0x0);
    VIASETREG(0x34, 0x0);
    VIASETREG(0x38, 0x0);
    VIASETREG(0x3c, 0x0);
    VIASETREG(0x40, 0x0);

    VIADisableMMIO(ctx);

    /* Init AGP and VQ regs */
    VIASETREG(0x43c, 0x00100000);
    VIASETREG(0x440, 0x00000000);
    VIASETREG(0x440, 0x00333004);
    VIASETREG(0x440, 0x60000000);
    VIASETREG(0x440, 0x61000000);
    VIASETREG(0x440, 0x62000000);
    VIASETREG(0x440, 0x63000000);
    VIASETREG(0x440, 0x64000000);
    VIASETREG(0x440, 0x7D000000);

    VIASETREG(0x43c, 0xfe020000);
    VIASETREG(0x440, 0x00000000);

    if (pVia->VQStart != 0) {
        /* Enable VQ */
        dwVQStartAddr = pVia->VQStart;
        dwVQEndAddr = pVia->VQEnd;
        dwVQStartL = 0x50000000 | (dwVQStartAddr & 0xFFFFFF);
        dwVQEndL = 0x51000000 | (dwVQEndAddr & 0xFFFFFF);
        dwVQStartEndH = 0x52000000 | ((dwVQStartAddr & 0xFF000000) >> 24) |
                        ((dwVQEndAddr & 0xFF000000) >> 16);
        dwVQLen = 0x53000000 | (VIA_VQ_SIZE >> 3);

        VIASETREG(0x43c, 0x00fe0000);
        VIASETREG(0x440, 0x080003fe);
        VIASETREG(0x440, 0x0a00027c);
        VIASETREG(0x440, 0x0b000260);
        VIASETREG(0x440, 0x0c000274);
        VIASETREG(0x440, 0x0d000264);
        VIASETREG(0x440, 0x0e000000);
        VIASETREG(0x440, 0x0f000020);
        VIASETREG(0x440, 0x1000027e);
        VIASETREG(0x440, 0x110002fe);
        VIASETREG(0x440, 0x200f0060);

        VIASETREG(0x440, 0x00000006);
        VIASETREG(0x440, 0x40008c0f);
        VIASETREG(0x440, 0x44000000);
        VIASETREG(0x440, 0x45080c04);
        VIASETREG(0x440, 0x46800408);

        VIASETREG(0x440, dwVQStartEndH);
        VIASETREG(0x440, dwVQStartL);
        VIASETREG(0x440, dwVQEndL);
        VIASETREG(0x440, dwVQLen);
    }
    else {
        /* Diable VQ */
        VIASETREG(0x43c, 0x00fe0000);
        VIASETREG(0x440, 0x00000004);
        VIASETREG(0x440, 0x40008c0f);
        VIASETREG(0x440, 0x44000000);
        VIASETREG(0x440, 0x45080c04);
        VIASETREG(0x440, 0x46800408);
    }

    dwGEMode = 0;

    switch (ctx->bpp) {
    case 16:
        dwGEMode |= VIA_GEM_16bpp;
        break;
    case 32:
        dwGEMode |= VIA_GEM_32bpp;
        break;
    default:
        dwGEMode |= VIA_GEM_8bpp;
        break;
    }

#if 0
    switch (ctx->shared.virtualWidth) {
    case 800:
        dwGEMode |= VIA_GEM_800;
        break;
    case 1024:
        dwGEMode |= VIA_GEM_1024;
        break;
    case 1280:
        dwGEMode |= VIA_GEM_1280;
        break;
    case 1600:
        dwGEMode |= VIA_GEM_1600;
        break;
    case 2048:
        dwGEMode |= VIA_GEM_2048;
        break;
    default:
        dwGEMode |= VIA_GEM_640;
        break;
    }
#endif
    
    VIAEnableMMIO(ctx);

    /* Set BPP and Pitch */
    VIASETREG(VIA_REG_GEMODE, dwGEMode);

    /* Set Src and Dst base address and pitch, pitch is qword */
    VIASETREG(VIA_REG_SRCBASE, 0x0);
    VIASETREG(VIA_REG_DSTBASE, 0x0);
    VIASETREG(VIA_REG_PITCH, VIA_PITCH_ENABLE |
              ((ctx->shared.virtualWidth * ctx->bpp >> 3) >> 3) |
              (((ctx->shared.virtualWidth * ctx->bpp >> 3) >> 3) << 16));
}

static int b3DRegsInitialized = 0;

static void VIAInitialize3DEngine(DRIDriverContext *ctx)
{
    VIAPtr  pVia = VIAPTR(ctx);
    int i;

    if (!b3DRegsInitialized)
    {

        VIASETREG(0x43C, 0x00010000);

        for (i = 0; i <= 0x7D; i++)
        {
            VIASETREG(0x440, (u_int32_t) i << 24);
        }

        VIASETREG(0x43C, 0x00020000);

        for (i = 0; i <= 0x94; i++)
        {
            VIASETREG(0x440, (u_int32_t) i << 24);
        }

        VIASETREG(0x440, 0x82400000);

        VIASETREG(0x43C, 0x01020000);


        for (i = 0; i <= 0x94; i++)
        {
            VIASETREG(0x440, (u_int32_t) i << 24);
        }

        VIASETREG(0x440, 0x82400000);
        VIASETREG(0x43C, 0xfe020000);

        for (i = 0; i <= 0x03; i++)
        {
            VIASETREG(0x440, (u_int32_t) i << 24);
        }

        VIASETREG(0x43C, 0x00030000);

        for (i = 0; i <= 0xff; i++)
        {
            VIASETREG(0x440, 0);
        }
        VIASETREG(0x43C, 0x00100000);
        VIASETREG(0x440, 0x00333004);
        VIASETREG(0x440, 0x10000002);
        VIASETREG(0x440, 0x60000000);
        VIASETREG(0x440, 0x61000000);
        VIASETREG(0x440, 0x62000000);
        VIASETREG(0x440, 0x63000000);
        VIASETREG(0x440, 0x64000000);

        VIASETREG(0x43C, 0x00fe0000);

        if (pVia->ChipRev >= 3 )
            VIASETREG(0x440,0x40008c0f);
        else
            VIASETREG(0x440,0x4000800f);

        VIASETREG(0x440,0x44000000);
        VIASETREG(0x440,0x45080C04);
        VIASETREG(0x440,0x46800408);
        VIASETREG(0x440,0x50000000);
        VIASETREG(0x440,0x51000000);
        VIASETREG(0x440,0x52000000);
        VIASETREG(0x440,0x53000000);

        b3DRegsInitialized = 1;
        xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                   "3D Engine has been initialized.\n");
    }

    VIASETREG(0x43C,0x00fe0000);
    VIASETREG(0x440,0x08000001);
    VIASETREG(0x440,0x0A000183);
    VIASETREG(0x440,0x0B00019F);
    VIASETREG(0x440,0x0C00018B);
    VIASETREG(0x440,0x0D00019B);
    VIASETREG(0x440,0x0E000000);
    VIASETREG(0x440,0x0F000000);
    VIASETREG(0x440,0x10000000);
    VIASETREG(0x440,0x11000000);
    VIASETREG(0x440,0x20000000);
}

static int
WaitIdleCLE266(VIAPtr pVia)
{
    int loop = 0;

    /*mem_barrier();*/

    while (!(VIAGETREG(VIA_REG_STATUS) & VIA_VR_QUEUE_BUSY) && (loop++ < MAXLOOP))
        ;

    while ((VIAGETREG(VIA_REG_STATUS) &
          (VIA_CMD_RGTR_BUSY | VIA_2D_ENG_BUSY | VIA_3D_ENG_BUSY)) &&
          (loop++ < MAXLOOP))
        ;

    return loop >= MAXLOOP;
}

static int viaInitFBDev(DRIDriverContext *ctx)
{
    VIAPtr pVia = CALLOC(sizeof(*pVia));

    ctx->driverPrivate = (void *)pVia;

    switch (ctx->chipset) {
    case PCI_CHIP_CLE3122:
    case PCI_CHIP_CLE3022:
        pVia->Chipset = VIA_CLE266;
        break;
    case PCI_CHIP_VT7205:
    case PCI_CHIP_VT3205:
        pVia->Chipset = VIA_KM400;
        break;
    case PCI_CHIP_VT3204:
        pVia->Chipset = VIA_K8M800;
        break;
    case PCI_CHIP_VT3259:
        pVia->Chipset = VIA_PM800;
        break;
    default:
        xf86DrvMsg(0, X_ERROR, "VIA: Unknown device ID (0x%x)\n", ctx->chipset);
    }

    /* _SOLO TODO XXX need to read ChipRev too */
    pVia->ChipRev = 0;

    pVia->videoRambytes = ctx->shared.fbSize;
    pVia->MmioBase = ctx->MMIOStart;
    pVia->FrameBufferBase = ctx->FBStart & 0xfc000000;

    pVia->FBFreeStart = ctx->shared.virtualWidth * ctx->cpp *
        ctx->shared.virtualHeight;

#if 1
    /* Alloc a second framebuffer for the second head */
    pVia->FBFreeStart += ctx->shared.virtualWidth * ctx->cpp *
	ctx->shared.virtualHeight;
#endif

    pVia->VQStart = pVia->FBFreeStart;
    pVia->VQEnd = pVia->FBFreeStart + VIA_VQ_SIZE - 1;

    pVia->FBFreeStart += VIA_VQ_SIZE;

    pVia->FBFreeEnd = pVia->videoRambytes;

    if (!VIADRIScreenInit(ctx))
        return 0;

    return 1;
}

static void viaHaltFBDev(DRIDriverContext *ctx)
{
    drmUnmap( ctx->pSAREA, ctx->shared.SAREASize );
    drmClose(ctx->drmFD);

    if (ctx->driverPrivate) {
        free(ctx->driverPrivate);
        ctx->driverPrivate = 0;
    }
}

static int viaEngineShutdown(const DRIDriverContext *ctx)
{
    return 1;
}

static int viaEngineRestore(const DRIDriverContext *ctx)
{
    return 1;
}

const struct DRIDriverRec __driDriver =
{
    viaValidateMode,
    viaPostValidateMode,
    viaInitFBDev,
    viaHaltFBDev,
    viaEngineShutdown,
    viaEngineRestore,  
    0,
};

