/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = VVBUFF.C
 *
 * DESCRIPTIVE NAME = Virtual Video Buffer Management routines
 *
 *
 * VERSION = V2.0
 *
 * DATE      11/10/88
 *
 * DESCRIPTION  This module contains the VVD's buffer management routines.
 *
 *
 * FUNCTIONS
 *              vvAllocShadowBuffer()  See if shadow buffer should be
 *                                     allocated/freed
 *              vvShrinkPlane()        Shrink an individual plane in the
 *                                     virtual buffer
 *              vvShrinkBuffer()       Shrink specified VDM's virtual buffer
 *              vvGrowPlane()          Grow an individual plane in the virtual
 *                                     buffer
 *              vvGrowBuffer()         Grow specified VDM's virtual buffer
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/


#include <mvdm.h>
#include <vvd.h>
#include "vvdp.h"

#ifdef   VDDSTRICT
MODNAME = __FILE__;
#endif

extern  ULONG   flVVD;                                          /*          */

#pragma  BEGIN_SWAP_DATA

extern PBYTE pPhysVRAM;
extern ULONG npgPlane;

#pragma  END_SWAP_DATA

#pragma  BEGIN_SWAP_CODE

/***************************************************************************
 *
 * FUNCTION NAME = vvAllocShadowBuffer()
 *
 * DESCRIPTION   = See if shadow buffer should be allocated/freed
 *
 *                 Allocate/free a shadow buffer for the current VDM.
 *                 Currently, if it is in CGAX or GRFX256 video modes AND
 *                 being windowed, then we allocate buffer if one does not
 *                 already exist (or reallocate if one exists but is now the
 *                       size); otherwise, we free any existing shadow
 *                 buffer.  It is assumed that PLANE0 is the plane we are
 *                 shadowing.
 *
 *                 Note that this routine should be called at the following
 *                 times:  whenever the video mode changes, whenever a
 *                 "windowed" request is made by the Shield, and whenever the
 *                 PLANE0 buffer is grown or shrunk.
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        = SUCCESS
 *                     TRUE
 *                 FAILURE
 *                     FALSE (not enough memory)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL PRIVENTRY vvAllocShadowBuffer(
  HVDM hvdm )
{
  PVOID p;
  BOOL fSuccess = TRUE;
  register PVDMDATA pvd = pVDMData(hvdm);


  if (pvd->anpgPlane[BANK0][PLANE0] && pvd->flVDMVideo&VDM_WINDOWED &&
     (pvd->mstateVideo == MEMORY_CGAX || pvd->mstateVideo == MEMORY_GRFX2 &&
     pvd->offOddBank || pvd->mstateVideo == MEMORY_GRFX256))
  {

    if (!pvd->npgShadowPlane)
    {

      if (p = VDHAllocPages(NULL,
                            pvd->anpgPlane[BANK0][PLANE0],
                            VDHAP_SYSTEM))
      {                                          /*                         */
        pvd->pShadowPlane = p;
        pvd->npgShadowPlane = pvd->anpgPlane[BANK0][PLANE0];/*              */
        pvd->flVDMVideo |= VDM_SHADOWINVALID;
      }

      else
        fSuccess = FALSE;
    }

    else
    {
      if (pvd->npgShadowPlane != pvd->anpgPlane[BANK0][PLANE0])
      {                                          /*                         */

        if (p = VDHReallocPages(pvd->pShadowPlane,
                                pvd->anpgPlane[BANK0][PLANE0],
                                NULL))
        {                                        /*                         */
          pvd->pShadowPlane = p;
          pvd->npgShadowPlane = pvd->anpgPlane[BANK0][PLANE0];/*            */
          pvd->flVDMVideo |= VDM_SHADOWINVALID;
        }

        else
        {
          fSuccess = FALSE;
          goto shadow_free;
        }
      }
    }
  }

  else
  {

    if (pvd->npgShadowPlane)
    {

shadow_free:

      VDHFreePages(pvd->pShadowPlane);
      pvd->pShadowPlane = NULL;
      pvd->npgShadowPlane = 0;
    }
  }
  return  fSuccess;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvShrinkPlane()
 *
 * DESCRIPTION   = Shrink an individual plane in the virtual buffer
 *
 *                 Shrink the specified plane to the specified # of pages,
 *                 and update the plane address and size if there is no
 *                 error.  If the # of pages is zero, all memory allocated
 *                 for the plane will be freed.
 *
 * INPUT         = hvdm   -> VDM
 *                 iPlane == plane #
 *                 nPages == # of pages
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *                 if  specified plane
 *                 if no error
 *                   update plane address and size
 *
 ****************************************************************************/

VOID PRIVENTRY vvShrinkPlane(
  HVDM hvdm,
  register INT iPlane,
  register INT iBank,
  INT nPages )
{
  PVOID p;
  register PVDMDATA pvd = pVDMData(hvdm);

  AssertFALSE(nPages < 0);

  /*
  ** If new size is zero
  */

  if (nPages == 0)
  {

    /*
    ** Free this plane buffer entirely
    */

    VDHFreePages(pvd->apPlane[iBank][iPlane]);   /*                         */

    /*
    ** Record NULL address and zero size
    */

    pvd->apPlane[iBank][iPlane] = NULL;          /*                         */
    pvd->anpgPlane[iBank][iPlane] = 0;           /*                         */
  }

  /*
  ** If there's still valid data
  */

  else
  {
    if (nPages < pvd->anpgPlane[iBank][iPlane])
    {                                            /*                         */

      /*
      ** Shrink the plane buffer
      */

      p = VDHReallocPages(pvd->apPlane[iBank][iPlane],
                          nPages,
                          NULL);                 /*                         */
      AssertNONZERO(p);

      /*
      ** Record (possibly new) address and minimum size
      */

      pvd->apPlane[iBank][iPlane] = p;           /*                         */
      pvd->anpgPlane[iBank][iPlane] = nPages;    /*                         */
    }
  }
  if (iPlane == PLANE0)
    vvAllocShadowBuffer(hvdm);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvShrinkBuffer()
 *
 * DESCRIPTION   = Shrink specified VDM's virtual buffer
 *
 *                 This routine's purpose is to shrink the virtual buffer to
 *                 a minimum possible size.  The minimum size needed for each
 *                 plane in the virtual buffer varies from plane to plane.
 *                 We scan the page-states for each plane from top to bottom,
 *                 searching for the first entry other than PAGE_NONE.  Then
 *                 we scan from the corresponding point in the plane down to
 *                 the bottom of the plane, looking for non-blank data.
 *                 Blank means spaces if the page-state was PAGE_TEXT, or
 *                 zeros if the page-state was PAGE_FONT or PAGE_GRFX.  From
 *                 the location of the first non-blank data, rounding up to
 *                 the nearest page boundary, all valid pages above that
 *                 point are marked PAGE_BLANK, and the plane buffer in
 *                 question is shrunk to that point.
 *
 * INPUT         = hvdm -> VDM
 *                 fAll == TRUE to shrink to nothing, FALSE for normal shrink
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *                 for every plane in the virtual buffer
 *                   scan top-down for non-blank
 *                   shrink plane down to that point
 *
 ****************************************************************************/

VOID PRIVENTRY vvShrinkBuffer(
  HVDM hvdm,
  BOOL fAll )
{
  PBYTE pbPlane;
  register INT iPage;
  INT i,iPlane;
  INT iBank;                                     /*                         */
  PSTATE ps;
  DWORD dw;
  register PDWORD pdw;
  register PVDMDATA pvd = pVDMData(hvdm);

#ifdef SVGA                                                     /*          */

  if( fAll && pvd->pLinearBuffer )
  {
    VDHFreePages( pvd->pLinearBuffer );
    pvd->pLinearBuffer = NULL;
    for( i = 0;
         i < MAX_LINEAR_BANKS;          /*            */
         i++ )
      pvd->abstate [i] = BANK_NONE;
  }

#endif                                                          /*          */

  /*
  ** For every bank                                                         
  */

  for (iBank = BANK0; iBank < MAX_BANKS; iBank++)
  {                                              /*                         */

    /*
    ** For every plane
    */

    for (iPlane = PLANE0; iPlane < MAX_PLANES; iPlane++)
    {

      /*
      ** Skip this plane if there is no buffer allocated
      */

      if (!pvd->apPlane[iBank][iPlane])          /*                         */
        continue;

      if (fAll)
        iPage = 0;

      else
      {
        ps = PAGE_NONE;
        iPage = pvd->anpgPlane[iBank][iPlane];   /*                         */
        pbPlane = pvd->apPlane[iBank][iPlane]+iPage *PAGESIZE;/*            */
      }

      /*
      ** For every page in the current plane
      */

      for (iPage--; iPage >= 0; iPage--,
           pbPlane -= PAGESIZE)
      {

        /*
        ** If this page is mapped to "virtual" physical memory,
        **   then we cannot decommit the corresponding virtual memory
        */

        if (pvd->aiVirtToPhys[iPage])
          break;

        /*
        ** Get page state
        */

        ps = (PSTATE)(pvd->aapstate[iBank][iPage][iPlane]);/*               */

        /*
        ** If page is valid, non-BLANK data that is NOT
        ** currently in VRAM (ie, resides in virtual memory)
        */

        if ((ps&PAGE_TYPE) && !(ps&(PAGE_BLANK|PAGE_INVRAM)))
        {

          /*
          ** Determine type of blank data
          */

          dw = 0;

          if ((ps&PAGE_TYPE) == PAGE_TEXT)
            dw = 0x07200720;

          /*
          ** Check the page for non-blank data
          */

          pdw = (PDWORD)pbPlane;

          for (i = PAGESIZE/sizeof(dw); i; i--)

            if (*--pdw != dw)
              break;

          if (!i)
            pvd->aapstate[iBank][iPage][iPlane] = (ps |= PAGE_BLANK);/*           */
        }

        /*
        ** If page is STILL valid, non-blank data, then exit loop
        */

        if ((ps&PAGE_TYPE) && !(ps&PAGE_BLANK))
          break;

#ifndef  AUTO_UNMAP

        else

          if (hvdm == CURRENT_VDM && pvd->aapstate[iBank][iPage][iPlane]
             &PAGE_VIRTUAL)                      /*                         */
            
            vvMapPage(iPage *PAGESIZE+pvd->pvdmPhysVRAMActive,
                      NULL,
                      ALL_PLANES,
                      BANK0,                     /*                         */
                      MAX_VGA_BANK,             /*            */
                      VDHMT_INVALID);
#endif
      }

      /*
      ** Shrink the plane down to the appropriate # of pages
      */

      vvShrinkPlane(hvdm,
                    iPlane,
                    iBank,
                    ++iPage);                    /*                         */
    }
  }                                              /*                         */
}

/***************************************************************************
 *
 * FUNCTION NAME = vvGrowPlane()
 *
 * DESCRIPTION   = Grow an individual plane in the virtual buffer
 *
 *                 Grow the specified plane to the specified # of pages, and
 *                 update the plane address and size if there is no error.
 *
 * INPUT         = hvdm   -> VDM
 *                 iPlane == plane #
 *                 nPages == # of pages
 *
 * OUTPUT        = SUCCESS
 *                     TRUE
 *                 FAILURE
 *                     FALSE (not enough memory)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *                 grow specified plane
 *                 if no error
 *                   update plane address and size
 *
 ****************************************************************************/

BOOL PRIVENTRY vvGrowPlane(
  HVDM hvdm,
  register INT iPlane,
  INT nPages,
  register INT iBank )
{
  PBVDM p = NULL;                       /*            */
  DWORD dw;
  PBYTE pExtended;
  INT nbExtended;
  BOOL fSuccess = FALSE;
  register PVDMDATA pvd = pVDMData(hvdm);

  /*
  ** If no plane buffer at all
  */

  if (!pvd->apPlane[iBank][iPlane])
  {                                              /*                         */

    /*
    ** First, allocate a maximum buffer
    */

    p = VDHAllocPages(NULL,
                      MAX_PAGESPERPLANE,
                      VDHAP_SYSTEM);

    /*
    ** Then shrink it down to the size needed
    ** This allows us to grow the object later up to the allocated
    ** maximum above without motion -- and most importantly, without
    ** panicking the kernel if there happen to mappings attached
    */

    if (p && nPages < MAX_PAGESPERPLANE)
      p = VDHReallocPages(p,
                          nPages,
                          NULL);
  }

  else
  {

    /*
    ** Or grow existing buffer to maximum size
    */

    if (pvd->anpgPlane[iBank][iPlane] >= nPages) /*                         */
      fSuccess = TRUE;

    else
    {
      AssertTRUE(nPages <= MAX_PAGESPERPLANE);
      p = VDHReallocPages(pvd->apPlane[iBank][iPlane],
                          nPages,
                          NULL);                 /*                         */
    }
  }

  /*
  ** If no error
  */

  if (!fSuccess && p)
  {
    pExtended = p+pvd->anpgPlane[iBank][iPlane]*PAGESIZE;/*                 */
    nbExtended = (nPages-pvd->anpgPlane[iBank][iPlane])*PAGESIZE;/*
                                                                            */

    /*
    ** Record (possibly new) address and maxed-out size
    */

    pvd->apPlane[iBank][iPlane] = p;             /*                         */
    pvd->anpgPlane[iBank][iPlane] = nPages;      /*                         */

    /*
    ** Now zero-fill the portion by which we just extended the buffer
    */

    dw = 0;

    if (pvd->mstateVideo <= MEMORY_TEXT && iPlane == PLANE0)
      dw = 0x07200720;
    vdhMoveMem(SSToDS(&dw),
               pExtended,
               0,
               sizeof(dw),
               nbExtended/sizeof(dw),
               FALSE);

    if (iPlane == PLANE0)
      vvAllocShadowBuffer(hvdm);
    fSuccess = TRUE;
  }
  return  fSuccess;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvGrowBuffer()
 *
 * DESCRIPTION   = Grow specified VDM's virtual buffer
 *
 *                 This routine's purpose is to grow the virtual buffer to
 *                 the maximum possible size needed.  On failure, we
 *                 deallocate whatever we successfully allocated and return
 *                 error.
 *
 * INPUT         = hvdm -> VDM
 *                 iPlane == specific plane (-1 for all)
 *                 nPages == # pages to grow (-1 for maximum)
 *
 * OUTPUT        = SUCCESS
 *                     TRUE
 *                 FAILURE
 *                     FALSE (not enough memory)
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *                 grow virtual buffer, plane by plane
 *                 if error
 *                   shrink any planes successfully grown
 *
 ****************************************************************************/

BOOL PRIVENTRY vvGrowBuffer(
  HVDM hvdm,
  register INT iPlane,
  INT nPages,
  INT iBank )                                                   /*          */
{
  register INT iPage;
  register PVDMDATA pvd = pVDMData(hvdm);               /*            */
#ifdef SVGA                                             /*            */
  if ((iBank >= MAX_BANKS) || (pvd->flVDMXVideo & VDMX_ENHANCEDMODE))     /*                        */
    return TRUE;                                        /*            */
#endif

  /*
  ** If no valid page size specified, use maximum
  */

  if (nPages < 0)
    nPages = npgPlane;

  /*
  ** If a specific plane was given, then grow only it
  */

  if (iPlane >= PLANE0)
  {
    return(vvGrowPlane(hvdm,
                        iPlane,
                        nPages,
                        iBank));
  }
  /*
  ** For every plane
  */
  for (iPlane = PLANE0; iPlane < MAX_PLANES; iPlane++)
  {
    /*
    ** Grow the plane
    */

    if (!vvGrowPlane(hvdm,
                     iPlane,
                     nPages,
                     iBank))
    {                                            /*                         */

      /*
      ** Shrink everything back down again
      */

      vvShrinkBuffer(hvdm,
                     FALSE);
      return  FALSE;
    }

    else
    {

      /*
      ** Clear all the blank bits for this plane
      */

      for (iPage = 0; iPage < MAX_PAGESPERPLANE; iPage++)
        pvd->aapstate[iBank][iPage][iPlane] &= ~PAGE_BLANK;
    }
  }
  return  TRUE;
}

#ifdef SVGA

/***************************************************************************
 *
 * FUNCTION NAME = vvGrowLinearBuffer()
 *
 * DESCRIPTION   = Grow specified VDM's virtual buffer for linear modes.
 *
 * INPUT         = hvdm -> VDM
 *                 iMaxBanks == no of 64k banks to allocate
 *
 * OUTPUT        = SUCCESS
 *                     TRUE
 *                 FAILURE
 *                     FALSE (not enough memory)
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *              if buffer already exists
 *                  return successful
 *              otherwise
 *                  allocate new buffer
 *                  initialise bank state table
 *
 ****************************************************************************/

BOOL PRIVENTRY vvGrowLinearBuffer(
  HVDM hvdm,
  register INT iMaxBanks )
{
  register INT iPages, i;
  register PVDMDATA pvd = pVDMData(hvdm);
  BOOL fRAMIsToast;

  iPages = iMaxBanks * PAGESFROMBYTES(64L*1024L);

  if( !pvd->pLinearBuffer )
    /* Previously fRAMIsToast was left undefined */             /*          */
    /* when allocation failed here! */                          /*          */
    fRAMIsToast =                                               /*          */
      !! (pvd->pLinearBuffer =                                  /*          */
            VDHAllocPages( NULL, iPages, VDHAP_SYSTEM ));
  else
    fRAMIsToast =                                               /*          */
      (pvd->ulLinearPages != iPages)                            /*          */
      && (pvd->pLinearBuffer =
            VDHReallocPages( pvd->pLinearBuffer, iPages, 0L ));
  if( fRAMIsToast )
  {
    pvd->ulLinearPages = iPages;
    for( i = 0;
         i < iMaxBanks;
         i++ )
      pvd->abstate [i] = BANK_INVALID;
    flVVD |= VVD_VRAMISTOAST;
  }
  return( !!pvd->pLinearBuffer );
}

#endif

#pragma  END_SWAP_CODE

