/*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.                                */
/*                                                                           */
/*****************************************************************************/
/**********************************************************************/
/*                                                                    */
/*   Module          = EDDQESC                                        */
/*                                                                    */
/*   Description     = Display Device Driver Escape functions:        */
/*                     Escape,                                        */
/*                     QueryEscSupport,                               */
/*                     QueryVIOCellSizes.                             */
/*                                                                    */
/*   Function        = Escape routes a call to an Escape Subfunction  */
/*                     using the given Escape Code.                   */
/*                     QueryEscSupport indicates which Escape         */
/*                     Subfunctions are supported.                    */
/*                     QueryVIOCellSizes returns the AVIO cell        */
/*                     sizes supported.                               */
/*                                                                    */
/*   Reference       = Winthorn Functional Specification              */
/*                     Device Driver Interface Specification          */
/*                     Display Device Driver Design Specification     */
/*                                                                    */
/*                                                                    */
/**********************************************************************/

#define INCL_GRE_DEVICE
#include <eddinclt.h>

#include <eddvcone.h>
#include <edddtypt.h>
#include <eddqextf.h>
#include <eddgextf.h>
#include <eddmextf.h>
#include <cursor.h>

extern DDTType             DDT;
extern AvioFontTableType   AvioBaseFont[NO_OF_AVIO_FONTS];
extern SHORT               softDrawInUse;
extern CURSORDATA          cursor_data;


#ifndef S3

/**********************************************************************/
/* Function prototype needed because of forward reference             */
/**********************************************************************/
ULONG SwitchBank(ULONG, PULONG);

#define APERTURE_SAVED 0x01

#ifndef   _8514
extern XGAINSTANCE  InstanceData;
extern ADAPTERINFO  aiXGAAdapter;
#else
extern ADAPTER8514  ai8514Adapter;
#endif
extern FSRSEM       FSRDriverSem;

typedef struct
{
  USHORT control;
  USHORT index;
  ULONG  flags;
} SAVEDAPERTURE;

/* Globals */
APERTURE best_aperture = { 0, 0, 0 };
SAVEDAPERTURE saved_aperture = { 0, 0, 0 };

/**********************************************************************/
/* Defect 66836 - We must set the PEL order field of the Memery       */
/* Access Mode Register to Intel format for the multi-media folks.    */
/* The         display driver is setting this field to Motorola       */
/* format when blitting in a seamless window.                         */
/*                                                                    */
/* Save a local copy of the Memory Access Mode Register               */
/**********************************************************************/
USHORT usMAMR;

/**********************************************************************/
/*                                                                    */
/*   4 SMV (software motion video) DevEscapes added for DCR 96:       */
/*                                                                    */
/*   - GETAPERTURE returns the size and physical address of the       */
/*     largest available aperture.                                    */
/*                                                                    */
/*   - ACQUIREFB allows the caller exclusive access to the adapter    */
/*                                                                    */
/*   - DEACQUIREFB releases the adapter for use by other components   */
/*                                                                    */
/*   - SWITCHBANK allows the caller to select which bank of VRAM is   */
/*     mapped into the aperture (64K only).                           */
/*                                                                    */
/**********************************************************************/

/***EP  GetAperture() - Get the video aperture
 *
 *  This DevEsc will be called be an application in order to get
 *  the aperture information.
 *
 *  ENTRY
 *      PULONG pcOutCount -> Pointer to a ULONG that contains
 *                           the number of bytes being requested.
 *
 *      PAPERTURE pAperture -> Pointer to the APERTURE structure
 *                                shown below. We will fill this in.
 *
 *      typedef struct
 *      {
 *        ULONG ulPhysAddr;      // Physical address of the VRAM
 *        ULONG ulApertureSize;  // Aperture size in bytes
 *        ULONG ulScanLineSize;  // Scan line size in bytes
 *        RECTL rctlScreen;      // Screen dimensions
 *      } APERTURE;
 *
 *      typedef APERTURE *PAPERTURE;
 *
 *
 *  EXIT
 *      SUCCESS - Structure is updated
 *
 *  Defects 62446 and 68657 - On some 386 SX machines, the VE bit of POS
 *  register base+4 is ON indicating that the 4M aperture is enabled.
 *  The 4M aperture should never be ON on an SX system because these
 *  systems have a 16 bit data bus.  This is a HW error probably located
 *  in the POST code. This problem is also carried over to systems with
 *  an XGA/2 adapter when DMQS is used.  The DMQS information is also
 *  incorrect under this circumstance.
 *
 *  As a workaround we check the 1M aperture first.  If the 1M aperture
 *  is not enabled we must check the system interface bus size via
 *  the Auto-config register.
 *
 *  BTW: This problem was first noticed on a model 57 SX machine.
 *
 */

ULONG GetAperture(PULONG pcOutCount, PAPERTURE pAperture)
{

  USHORT usACR;                          /* Aperture control register */

  #ifndef   _8514
  if ( InstanceData.XGA_1M_aperture_phys ) {

    pAperture->ulPhysAddr =
    best_aperture.ulPhysAddr = InstanceData.XGA_1M_aperture_phys;

    pAperture->ulApertureSize
    = best_aperture.ulApertureSize = 1 * 1024 * 1024;
    goto FOUND_APERTURE;

  }

  if (InstanceData.XGA_4M_aperture_phys) {

    outp(aiXGAAdapter.usIORegBase + XGA_IO_INDEX, 4);
    usACR = (USHORT)inp(aiXGAAdapter.usIORegBase + XGA_IO_INDEX + 1);
    usACR &= 9;                       /* Remove unwanted bits */

    if(usACR == 1) {                  /* We have a 32 bit system interface */
       pAperture->ulPhysAddr =
       best_aperture.ulPhysAddr = InstanceData.XGA_4M_aperture_phys;

       pAperture->ulApertureSize
       = best_aperture.ulApertureSize = 4 * 1024 * 1024;
       goto FOUND_APERTURE;
    }

  }

  /* Check if the 64K aperture has been enabled */

  usACR = (USHORT)inp(aiXGAAdapter.usIORegBase + XGA_APERTURE_CONTROL);

  usACR &= (A0000_APERTURE+B0000_APERTURE);   /* Remove unwanted bits */

  if(usACR == A0000_APERTURE)
     pAperture->ulPhysAddr = best_aperture.ulPhysAddr = 0xa0000;

  else if (usACR == B0000_APERTURE)
     pAperture->ulPhysAddr = best_aperture.ulPhysAddr = 0xb0000;

  else if (!usACR) {                /* No aperture enabled */
     /***********************************************************/
     /* Defect 59723                                            */
     /* If no apertures are enabled then we will go ahead and   */
     /* enable the 64K aperture using the 0xB0000 address       */
     /***********************************************************/
     outp(aiXGAAdapter.usIORegBase + XGA_APERTURE_CONTROL, B0000_APERTURE);
     pAperture->ulPhysAddr = best_aperture.ulPhysAddr = 0xb0000;
  }
  pAperture->ulApertureSize = best_aperture.ulApertureSize = 64 * 1024;

  #else
  #ifdef S3
  if ( ai8514Adapter.ONE_M_aperture_phys ) {

    pAperture->ulPhysAddr =
    best_aperture.ulPhysAddr = ai8514Adapter.ONE_M_aperture_phys;

    pAperture->ulApertureSize
    = best_aperture.ulApertureSize = 1 * 1024 * 1024;
    goto FOUND_APERTURE;

  }

  if ( ai8514Adapter.FOUR_M_aperture_phys ) {

    pAperture->ulPhysAddr =
    best_aperture.ulPhysAddr = ai8514Adapter.FOUR_M_aperture_phys;

    pAperture->ulApertureSize
    = best_aperture.ulApertureSize = 4 * 1024 * 1024;
    goto FOUND_APERTURE;

  }

  #else
  pAperture->ulPhysAddr = best_aperture.ulPhysAddr = 0L;
  pAperture->ulApertureSize = best_aperture.ulApertureSize = 0L;
  #endif
  #endif


FOUND_APERTURE:
  pAperture->ulScanLineSize = DDT.ScreenWidth;
  pAperture->rctlScreen.xLeft = 0;
  pAperture->rctlScreen.xRight = DDT.ScreenWidth-1;
  pAperture->rctlScreen.yBottom = DDT.ScreenHeight-1;
  pAperture->rctlScreen.yTop = 0;

  *pcOutCount = sizeof(APERTURE);

  return(DEV_OK);

} /* GetAperture */



/***EP  AcquireFB() - Acquire the frame buffer
 *
 *  This DevEsc will be called be an application in order to get
 *  exclusive access to the frame buffer.
 *
 *  ENTRY
 *      ULONG cInCount - Bytes count of caller data
 *
 *      PACQUIREFB pAcquireFB - Pointer to the ACQUIREFB structure.
 *                              This structure is shown below and defined
 *                              in PMDEV.H.
 *
 *  typedef struct
 *  {
 *    ULONG fAFBFlags;    // Action flags
 *    ULONG ulBankNumber; // Which bank # is being requested
 *                        //-----------------------------------------------
 *                        // This is needed so they don't have to switch
 *                        // banks before aquireing the frame buffer.
 *                        //-----------------------------------------------
 *    RECTL rctlXRegion;  // Cursor Xclude region.
 *  } ACQUIREFB;       // Aquire frame buffer
 *
 *  typedef ACQUIREFB *PACQUIREFB;
 *
 *  Where fAFBFlags bits are defined as;
 *
 *  Bit 0 - (AFB_SWITCH = 1)  // Switch bank before giving access
 *          OFF               // No bank switching.
 *
 *  EXIT
 *      SUCCESS
 *          return(DEV_OK) - App now has exclusive access to the frame buffer.
 *      FAILURE
 *          LogError(PMERR_INV_ESCAPE_DATA);
 *          return(DEVESC_ERROR);
 *
 */
#ifdef XGA
ULONG AcquireFB(ULONG cInCount, PACQUIREFB pAcquireFB)
{
  register USHORT sem_result;

  IgnoreParam(cInCount);

  sem_result = RequestDriverSemaphore();

  if ( sem_result != NULL )
  {
    /**************************************************************/
    /* Failed to get the semaphore.                               */
    /**************************************************************/
    return(DEVESC_ERROR);
  }

  /****************************************************************/
  /* Take a copy of the existing aperture control and index       */
  /* registers so they can be restored in DeacquireFB.            */
  /****************************************************************/
  if ( !(saved_aperture.flags & APERTURE_SAVED))
  {
    saved_aperture.control = (USHORT)inp(aiXGAAdapter.usIORegBase +
                                         XGA_APERTURE_CONTROL);
    saved_aperture.index =
         (USHORT)inp(aiXGAAdapter.usIORegBase + XGA_APERTURE_INDEX);
  }

  switch(best_aperture.ulApertureSize)
  {
        case 4 * 1024 * 1024:

        /************************************************************/
        /* 4M aperture: disable 1M and 64k apertures and write 0 to */
        /* the index register (this makes it unnecessary to call    */
        /* SwitchBank for a 4M aperture).                           */
        /************************************************************/

            outp(aiXGAAdapter.usIORegBase + XGA_APERTURE_CONTROL,
                 NO_64K_APERTURE);
            outp(aiXGAAdapter.usIORegBase + XGA_APERTURE_INDEX, 0);
            break;

        case 1 * 1024 * 1024:

        /************************************************************/
        /* 1M aperture: disable the 64K aperture and write 0 to the */
        /* index register (this makes it unnecessary to call        */
        /* SwitchBank for a 1M aperture).                           */
        /************************************************************/

            outp(aiXGAAdapter.usIORegBase + XGA_APERTURE_CONTROL,
                 NO_64K_APERTURE);
            outp(aiXGAAdapter.usIORegBase + XGA_APERTURE_INDEX, 0);
            break;

        case 64 * 1024:

        /************************************************************/
        /* 64K aperture: select the aperture at 0xb0000. The index  */
        /* register is set on a SwitchBank call, ie the selected    */
        /* bank is undefined after this call.                       */
        /************************************************************/

            outp(aiXGAAdapter.usIORegBase + XGA_APERTURE_CONTROL,
                B0000_APERTURE);
            break;

        default:

      /****************************************************************/
      /* Invalid: either something has gone       or GetAperture has  */
      /* not been called.                                             */
      /****************************************************************/

            LogError(PMERR_INV_ESCAPE_DATA);
            return(DEVESC_ERROR);
  }

  saved_aperture.flags |= APERTURE_SAVED;

  /* Defect 64434 - The SwitchBank needs to take place after the aperture
     information has been saved.
  */
  if(pAcquireFB->fAFBFlags & AFB_SWITCH) {
     if( SwitchBank(sizeof(ULONG), &pAcquireFB->ulBankNumber) == DEVESC_ERROR )
        return(DEVESC_ERROR);
  }

  /******************************************************************/
  /* If we are using a software cursor we will update the cursor    */
  /* exclude region. UpdateXcludeRegion will remove the cursor      */
  /* if it is currently in this region.                             */
  /******************************************************************/
  if ( cursor_data.cursor_status & CURSOR_SOFTWARE)
     UpdateXcludeRegion(pAcquireFB->rctlXRegion);

  /******************************************************************/
  /* Defect 66836 - Save the original pel order format, and then    */
  /* reset the adapter into Intel pel order format if it was in     */
  /* Motorola format.                                               */
  /******************************************************************/
  usMAMR = (USHORT)inp(aiXGAAdapter.usIORegBase + XGA_MEMORY_ACCESS_MODE);

  if(usMAMR & PEL_ORDER_MOTOROLA)
     outp(aiXGAAdapter.usIORegBase + XGA_MEMORY_ACCESS_MODE,
          (usMAMR & ~PEL_ORDER_MOTOROLA));

  return(DEV_OK);

} /* AcquireFB */



/***EP  DeacquireFB() - Release the frame buffer
 *
 *  This DevEsc will be called by an application when it wants
 *  to release its access to the frame buffer.
 *
 *  ENTRY
 *      No input parms
 *
 *  EXIT
 *      SUCCESS
 *          return(DEV_OK)
 *      FAILURE
 *          LogError(PMERR_INV_ESCAPE_DATA);
 *          return(DEVESC_ERROR);
 *
 */

ULONG DeacquireFB(VOID)
{
  ULONG rc = DEV_OK;
  USHORT usage=FSRDriverSem.Usage;

  if (saved_aperture.flags & APERTURE_SAVED)
  {
      /****************************************************************/
      /* Restore the aperture control and index registers to their    */
      /* values before the AcquireFB.                                 */
      /****************************************************************/

        outp(aiXGAAdapter.usIORegBase + XGA_APERTURE_CONTROL,
                                                saved_aperture.control);
        outp(aiXGAAdapter.usIORegBase + XGA_APERTURE_INDEX,
                                                  saved_aperture.index);
        ReleaseDriverSemaphore();

        /*********************************************************************/
        /* If A has grabbed the semaphore but B tries to clear it then we    */
        /* will end up here - the semaphore will not be cleared, but unless  */
        /* precautions are taken RESET_FB_ACQUIRED will be called, with the  */
        /* result that when A (legitimately) tries to clear the semaphore,   */
        /* the "if" statement at the top of this block won't let him - a     */
        /* complete deadlock with then ensue. Thus the usage count in        */
        /* FSRDriverSem is checked to see if it has changed.                 */
        /*********************************************************************/

        if (FSRDriverSem.Usage == usage)
            rc = DEVESC_ERROR;
        else
            saved_aperture.flags &= ~APERTURE_SAVED;

  }
  else

  /***************************************************************************/
  /* Invalid : either something has gone       or GetAperture has not been   */
  /*           called                                                        */
  /***************************************************************************/

  {
      LogError(PMERR_INV_ESCAPE_DATA);
      return(DEVESC_ERROR);
  }

  /******************************************************************/
  /* No more exclude region                                         */
  /******************************************************************/
  cursor_data.cursor_status &= ~CURSOR_XREGION;

  /******************************************************************/
  /* Defect 66836 - Restore the original pel order format           */
  /******************************************************************/
  outp(aiXGAAdapter.usIORegBase + XGA_MEMORY_ACCESS_MODE, usMAMR );

  return(rc);

} /* DeacquireFB */



/***EP  SwitchBank() - Switch to a specific bank number.
 *
 *  This DevEsc will be called be an application when it wants to
 *  switch to a specific bank.
 *
 *  ENTRY
 *      ULONG cInCount - Bytes count of caller data
 *
 *      PULONG pInData - Pointer to a ULONG containing the bank number.
 *
 *  EXIT
 *      SUCCESS
 *          return(DEV_OK) - Bank has been switch successfully.
 *      FAILURE
 *          LogError(PMERR_INV_ESCAPE_DATA);
 *          return(DEVESC_ERROR);
 *
 */

ULONG SwitchBank(ULONG cInCount, PULONG pInData)
{
  ULONG MaxBankNo = aiXGAAdapter.lMemorySize/0x10000ul;

  if (MaxBankNo==0) MaxBankNo=1;

  IgnoreParam(cInCount);
  /********************************************************************/
  /* Frame buffer must be acquired and bank number must be valid...   */
  /********************************************************************/
  if ( !(saved_aperture.flags & APERTURE_SAVED))
  {
    LogError(PMERR_INV_ESCAPE_DATA);
    return(DEVESC_ERROR);
  }

  /********************************************************************/
  /* Only the 64K aperture needs bank switching                       */
  /********************************************************************/
  if ( best_aperture.ulApertureSize == 64 * 1024 )
  {
    if ( *pInData > MaxBankNo)
    {
      LogError(PMERR_INV_ESCAPE_DATA);
      return(DEVESC_ERROR);
    }

    outp(aiXGAAdapter.usIORegBase + XGA_APERTURE_INDEX,
         *(PUSHORT)pInData);
  }

  return(DEV_OK);

} /* SwitchBank */

#endif
/**********************************************************************/
/*                                                                    */
/*   Escape will (depending on the given Escape Code):                */
/*                                                                    */
/*   - route the call to an Escape Subfunction and return the result  */
/*     (and the altered parameters)                                   */
/*                                                                    */
/*   - return DEVESC_NOTIMPLEMENTED if the requested Escape           */
/*     Subfunction is not supported by the driver                     */
/*                                                                    */
/**********************************************************************/

DDIENTRY eddq_Escape (HDC       hdc,
                      ULONG     ArgEscape,
                      ULONG     ArgInCount,
                      PULONG    ArgInData,
                      PULONG    ArgOutCount,
                      PULONG    ArgOutData,
                      PDC       pdcArg,
                      ULONG     FunN)

{
    ULONG   retval;

    /******************************************************************/
    /* For DCR 96: add the SwitchBank function. This must be at ring  */
    /* 2 in order to do the in and out to the XGA indexed registers.  */
    /* If this is called at ring3 then making a call to pmwin for     */
    /* VisRegionNotify causes pmwin to trap. Avoid this by setting    */
    /* the DONT_CLEAN option on EnterDriver - this is OK as long as a */
    /* DevEscape is never added which does drawing using the clip     */
    /* region.                                */
    /******************************************************************/

    #define FLAGS EDF_STANDARD | EDF_DONT_CLEAN

    IgnoreParam(hdc);

    /******************************************************************/
    /* Select function according to ArgEscape.                        */
    /******************************************************************/
    switch (ArgEscape)
    {

    case DEVESC_QUERYESCSUPPORT:
        EnterDriver(pdcArg, FunN, FLAGS);
        retval = eddq_QueryEscSupport(ArgInData);
        ExitDriver(pdcArg, FunN,  FLAGS);
        break;

    case DEVESC_QUERYVIOCELLSIZES:
        EnterDriver(pdcArg, FunN, FLAGS);
        retval = eddq_QueryVIOCellSizes(ArgOutCount, ArgOutData);
        ExitDriver(pdcArg, FunN,  FLAGS);
        break;

    #ifdef XGA
    case DEVESC_GETAPERTURE:
        EnterDriver(pdcArg, FunN, FLAGS);
        retval = GetAperture(ArgOutCount, (PVOID)ArgOutData);
        ExitDriver(pdcArg, FunN,  FLAGS);
        break;

    case DEVESC_ACQUIREFB:
        retval = AcquireFB(ArgInCount, (PVOID)ArgInData);
        break;

    case DEVESC_DEACQUIREFB:
        retval = DeacquireFB();
        break;

    case DEVESC_SWITCHBANK:
        retval = SwitchBank(ArgInCount, ArgInData);
        break;
    #endif

    default:
        retval = DEVESC_NOTIMPLEMENTED;
        break;
    }

    return(retval);
}



/**********************************************************************/
/*                                                                    */
/*   QueryEscSupport returns a value indicating whether a particular  */
/* Escape Subfunction, referred to by InData, is supported or not by  */
/* the display driver.                                                */
/*                                                                    */
/*   OutCount and OutData are not set or used by QueryEscSupport.     */
/*                                                                    */
/**********************************************************************/
ULONG DRIVERCALL eddq_QueryEscSupport (PULONG ArgInData)
{
    /******************************************************************/
    /* The desired escape function is indicated by ArgInData.         */
    /******************************************************************/
    switch (*ArgInData)
    {

    case DEVESC_QUERYESCSUPPORT:
    case DEVESC_QUERYVIOCELLSIZES:
    #ifdef XGA
    case DEVESC_GETAPERTURE:
    case DEVESC_ACQUIREFB:
    case DEVESC_DEACQUIREFB:
    case DEVESC_SWITCHBANK:
    #endif
        return(DEV_OK);

    default:
        return(DEVESC_NOTIMPLEMENTED);
    }
}

#endif // ~S3

/**********************************************************************/
/*                                                                    */
/*   QueryVIOCellSizes returns data describing the VIO cell sizes     */
/* supported by the driver for the display attached.  The data        */
/* returned depends on OutCount.                                      */
/*                                                                    */
/*   InCount and InData are not set or used by QueryVIOCellSizes.     */
/*                                                                    */
/**********************************************************************/

ULONG DRIVERCALL eddq_QueryVIOCellSizes (PULONG ArgOutCount,
                                         PULONG ArgOutData)
{
    /******************************************************************/
    /* Local variables                                                */
    /******************************************************************/
    register ULONG   Count;           /* no of cell sizes returned    */
    register ULONG   i;               /* loop control variable        */

    /******************************************************************/
    /* The data returned depends on OutCount                          */
    /******************************************************************/
    if (*ArgOutCount < 4)
    {
        *ArgOutCount = 0;
    }
    else
    {
        *ArgOutData++ = MAX_AVIO_FONTS_LOADED;

        if (*ArgOutCount < 8)
        {
            *ArgOutCount = 4;
        }
        else
        {
            Count = min( MAX_AVIO_FONTS_LOADED,
                         ((*ArgOutCount - 8) / 8) );
            *ArgOutData++ = Count;
            *ArgOutCount  = 8 + 8 * Count;

            if ( DDT.ScreenWidth < HI_RES_WIDTH )
            {
              i = 0;
            }
            else
            {
              i = NO_OF_AVIO_FONTS - MAX_AVIO_FONTS_LOADED;
              Count = MAX_AVIO_FONTS_LOADED + i;
            }
            for ( ; i < Count; i++)
            {
                *ArgOutData++ = AvioBaseFont[i].CellWidth;
                *ArgOutData++ = AvioBaseFont[i].CellHeight;
            }
        }
    }

    return( DEV_OK );
}
