text: /* $XFree86: xc/programs/Xserver/hw/xfree86/accel/p9000/p9000init.c,v 3.15 1996/12/23 06:40:46 dawes Exp $ */
  /*
   * Copyright 1994 Erik Nygren (nygren@mit.edu)
   *
   * This code may be freely incorporated in any program without royalty, as
   * long as the copyright notice stays intact.
   *
   * Additions by Harry Langenbacher (harry@brain.jpl.nasa.gov)
   *
   * ERIK NYGREN AND HARRY LANGENBACHER
   * DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
   * WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL ERIK NYGREN
   * OR HARRY LANGENBACHER BE LIABLE
   * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
   * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
   * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
   * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   */
  /* $XConsortium: p9000init.c /main/11 1996/05/07 17:11:31 kaleb $ */
  
  #include "X.h"
  #include "input.h"
  
  #include "xf86.h"
  #include "xf86_OSlib.h"
  
  #include "p9000.h"
  #include "p9000reg.h"
  
  #define XCONFIG_FLAGS_ONLY
  #include "xf86_Config.h"
  
  extern p9000CRTCRegRec  p9000CRTCRegs;
  
  static short p9000Inited = FALSE;
  
  int p9000InitCursorFlag = TRUE;
  
  extern pointer vgaBase;
  
  /* prototypes */
  
  static void p9000ProbeMemConfig(
  #if NeedFunctionPrototypes
     void
  #endif
  );
  
  /*	
   * p9000CalcSysconfigHres --
   *    Calculates the horizontal resolution component of the sysconfig
   *    register.  Returns whether the horiz res is possible or not.
   *    Stores the value in sysconfigval.
   */
  Bool p9000CalcSysconfigHres(hres, bytesperpixel, sysconfigval)
       int hres; 
       unsigned long bytesperpixel;
       unsigned long *sysconfigval;
  {
    static struct p9000SysconfigHresFieldEntry {
      unsigned long fieldval;  /* The value to store in the field */
      int           add;       /* How much to add for this field */
      Bool          specin[3]; /* Which fields can this be spec'd in */
    } p9000SysconfigHresFields[] = {
      /* val  add     0      1      2  */
      { 0x7, 2048, {FALSE, FALSE, TRUE}  },
      { 0x6, 1024, {FALSE, TRUE,  TRUE}  },
      { 0x5, 512,  {TRUE,  TRUE,  TRUE}  },
      { 0x4, 256,  {TRUE,  TRUE,  TRUE}  },
      { 0x3, 128,  {TRUE,  TRUE,  TRUE}  },
      { 0x2, 64,   {TRUE,  TRUE,  FALSE} },
      { 0x1, 32,   {TRUE,  FALSE, FALSE} },
      { 0x0, 0,    {TRUE,  TRUE,  TRUE}  },
    };
    int p9000SysconfigHresFieldEntries = sizeof(p9000SysconfigHresFields)
      / sizeof(struct p9000SysconfigHresFieldEntry);
    int remhres;                     /* The remaining hres */
    unsigned long cursysconfig = 0;  /* The current version of sysconfig */
    int curfield, curfv;             /* The current field and field value */
    
    remhres = bytesperpixel*hres;
  
    /* NOTE - the p9000 manual figure 60 has the fields labeled
       backwards - but if you look at values from viper.ini
       you'll see that it's the left-most (msb) field that
       can have values up to 7 , so that must be field 3 . It
       doesn't work for rectangles if you mess this up, i.e. if
       you calculate a wrong number or you use a field value
       illegal for that field */
  
    for (curfield = 2; curfield >=0; curfield--)
      for (curfv = 0; curfv < p9000SysconfigHresFieldEntries; curfv++)
        if (p9000SysconfigHresFields[curfv].specin[curfield]
  	  && (p9000SysconfigHresFields[curfv].add <= remhres))
  	{
  	  remhres -= p9000SysconfigHresFields[curfv].add;
  	  cursysconfig |= 
  	    p9000SysconfigHresFields[curfv].fieldval << (curfield*3 + 14);
  	  break;
  	}
    if (remhres)
      {
        ErrorF("\tIt is not possible to have a horiz resolution\n\tof %d at %d bytes per pixel.  Try a horizontal\n\tresolution of %d instead.\n",
  	     hres, bytesperpixel, hres - remhres/bytesperpixel);
        return(FALSE);
      }
    *sysconfigval = cursysconfig;
    return(TRUE);
  }
  
  
  /*	
   * p9000CalcMiscRegs --
   *    Initializes the P9000 for the parameters which do not
   *    depend on video mode, such as memory size.
   */
  void p9000CalcMiscRegs()
  {
    /* figure out how much memory the board has */
    p9000ProbeMemConfig();
    if (p9000MiscReg.memconfig == 0x2L)	      /* If 2 Meg. Byte */
      p9000MiscReg.srtctl = 0x000001E5L;
    else
      p9000MiscReg.srtctl = 0x000001E4L;   /* else is 1 meg */
    if (xf86Verbose)
      ErrorF("%s %s: MemSize: 0x%x MemConfig: 0x%x\n",
  	   XCONFIG_GIVEN, p9000InfoRec.name,
  	   p9000MiscReg.memsize, p9000MiscReg.memconfig);
  }
  
  
  /*
   * p9000CalcCRTCRegs --
   *     Initializes the P9000 for the currently selected CRTC parameters.
   *     Thanks to Harry Langenbacher for figuring this out!
   */
  void p9000CalcCRTCRegs(crtcRegs, mode)
       p9000CRTCRegPtr crtcRegs;
       DisplayModePtr mode;
  {
  
    crtcRegs->XSize = mode->CrtcHDisplay;
    crtcRegs->YSize = mode->CrtcVDisplay;
    crtcRegs->BytesPerPixel = p9000InfoRec.bitsPerPixel / 8;
    
    /* Where the BytesPerPixel gets multiplied in SHOULD CHANGE!!! *TO*DO* */
    crtcRegs->hrzt  = (crtcRegs->BytesPerPixel) * (mode->CrtcHTotal / 4) - 1 ;
    crtcRegs->hrzsr = (crtcRegs->BytesPerPixel) * 
      ((mode->CrtcHSyncEnd - mode->CrtcHSyncStart) / 4 ) - 1 ;
    crtcRegs->hrzbr = (crtcRegs->BytesPerPixel) * 
      ((mode->CrtcHTotal - mode->CrtcHSyncStart) / 4 ) - 1 ;
    crtcRegs->hrzbf = (crtcRegs->BytesPerPixel) * 
      ((mode->CrtcHDisplay+(mode->CrtcHTotal-mode->CrtcHSyncStart)) /4) - 1 ;
  
    crtcRegs->vrtt  = mode->CrtcVTotal ;
    crtcRegs->vrtsr = mode->CrtcVSyncEnd - mode->CrtcVSyncStart ;
    crtcRegs->vrtbr = mode->CrtcVTotal - mode->CrtcVSyncStart ;
    crtcRegs->vrtbf = crtcRegs->vrtbr + mode->CrtcVDisplay ;
    crtcRegs->prehrzc = 0x0L;
    crtcRegs->prevrtc = 0x0L;
  
    if (mode->Flags & V_NHSYNC) crtcRegs->hp = SP_NEGATIVE;
    else crtcRegs->hp = SP_POSITIVE;
  
    if (mode->Flags & V_NVSYNC) crtcRegs->vp = SP_NEGATIVE;
    else crtcRegs->vp = SP_POSITIVE;
  
    if (mode->Flags & V_INTERLACE) crtcRegs->interlaced = TRUE;
    else crtcRegs->interlaced = FALSE;
  
    /* Calculate sysconfig */
    if (!p9000CalcSysconfigHres(mode->CrtcHDisplay, (int)crtcRegs->BytesPerPixel,
  			      &crtcRegs->sysconfig))
      ErrorF("Bad horizontal resolution!!!!\n");
  #ifdef DEBUG
    ErrorF("p9000CalcSysconfigHres returned 0x%lx\n", crtcRegs->sysconfig);
  #endif
    crtcRegs->sysconfig |= 0x00003000;
  
    /* This is set to the default for now. *TO*DO* */
    crtcRegs->memspeed = MEMSPEED;
    /* Get the dot clock */
    crtcRegs->dotfreq = mode->SynthClock * 1000;
  
  #ifdef DEBUG
    ErrorF("The value of mode->Clock is %d.  sysconfig is %lx. Dotfreq is %ld\n",
  	 mode->Clock,crtcRegs->sysconfig,crtcRegs->dotfreq);
    ErrorF("Using %dx%d screen\n",mode->HDisplay,mode->VDisplay);
    ErrorF("HRZT %lx  HRZSR %lx  HRZBR %lx   HRZBF %lx   \nVRTSR %lx  VRTBR %lx   VRTBF %lx  VRTT: %lx\n",
  	 crtcRegs->hrzt, crtcRegs->hrzsr, crtcRegs->hrzbr, crtcRegs->hrzbf,
  	 crtcRegs->vrtsr,
  	 crtcRegs->vrtbr, crtcRegs->vrtbf, crtcRegs->vrtt);
  #endif
  }
  
  
  /* 
   * p9000SetCRTCRegs --
   *     Initializes the p9000 for the currently selected CRTC parameters.
   */
  void p9000SetCRTCRegs(crtcRegs)
       p9000CRTCRegPtr crtcRegs;
  {
    unsigned long ClipMax;
  
    /* System Configuration (width, byte order) */
    p9000Store(SYSCONFIG,CtlBase,crtcRegs->sysconfig);
  
    /* Disable interrupts from P9000 */
    p9000Store(INTERRUPT_EN,CtlBase,0x00000080L);
  
    /* Write Horizontal Timing Registers */
    p9000Store(PREHRZC,CtlBase, crtcRegs->prehrzc);
    p9000Store(HRZSR,CtlBase, crtcRegs->hrzsr);	/* HRZSR */
    p9000Store(HRZBR,CtlBase, crtcRegs->hrzbr);	/* HRZBR */
    p9000Store(HRZBF,CtlBase, crtcRegs->hrzbf);	/* HRZBF */
    p9000Store(HRZT,CtlBase, crtcRegs->hrzt);	/* HRZT  */
  
    /* Write Vertical Timing Registers */
    p9000Store(PREVRTC,CtlBase, crtcRegs->prevrtc); 
    p9000Store(VRTSR,CtlBase, crtcRegs->vrtsr);	/* VRTSR */
    p9000Store(VRTBR,CtlBase, crtcRegs->vrtbr);	/* VRTBR */
    p9000Store(VRTBF,CtlBase, crtcRegs->vrtbf);	/* VRTBF */
    p9000Store(VRTT,CtlBase, crtcRegs->vrtt);	/* VRTT  */
  
    /* Memory Configuration */
    p9000Store(MEM_CONFIG,CtlBase, p9000MiscReg.memconfig);
  
    p9000Store(RFPERIOD,CtlBase, 0x00000186L);
    p9000Store(RLMAX,CtlBase, 0x000000FAL);
  
    /* Enable writing to all 8 planes */
    p9000Store(PMASK,CtlBase,0x000000FFL);
    /* Select buffer 1 and allow drawing in window */
    p9000Store(DRAW_MODE,CtlBase,0x0000000AL);
    /* Disable any window (coord) offset */
    p9000Store(W_MIN,CtlBase,0x00000000L);
    /* Set the pattern origin to 0,0 */
    p9000Store(PAT_ORIGINX,CtlBase,0x00000000L);
    p9000Store(PAT_ORIGINY,CtlBase,0x00000000L); 
  
    /* Calulate and set registers for clipping */
    ClipMax= ((long)(crtcRegs->XSize*crtcRegs->BytesPerPixel)-1)<<16
      | ( p9000MiscReg.memsize / (crtcRegs->XSize*crtcRegs->BytesPerPixel) - 1);
    p9000Store(W_MIN,CtlBase,0);        /* Minimum Clipping */
    p9000Store(W_MAX,CtlBase,ClipMax);  /* Maximum Clipping */
  #ifdef DEBUG
    ErrorF("ClipMax set to 0x%lx\n",ClipMax);
  #endif
  
    /* Clear the screen the first time before enabling video */
    if (!p9000Inited)
      p9000ClearScreen();
  
    /* Enable video (1e5, 1e4, or 1c4) */
    p9000Store(SRTCTL,CtlBase, p9000MiscReg.srtctl);
  
  #ifdef DEBUG
    ErrorF("About to set the clock\n");
  #endif
    p9000VendorPtr->SetClock(crtcRegs->dotfreq, crtcRegs->memspeed);
  
    p9000InitCursorFlag = TRUE;
    p9000Inited = TRUE;
  }
  
  
  /* 
   * p9000InitAperture --
   *     Initialize the aperture for the p9000.
   */
  void p9000InitAperture(screen_idx)
      int screen_idx;
  {
    /* Map standard VGA in for saving and restoration */
    vgaBase = xf86MapVidMem(screen_idx, VGA_REGION, (pointer)0xA0000,
  			  0x10000);  /* 64k Banks */
  
    /* Map the P9000 in */
    p9000VideoMem = xf86MapVidMem(screen_idx, LINEAR_REGION, 
  				(pointer)(p9000InfoRec.MemBase),
  				0x400000L);  /* 4 Megabytes */
    
    /* the start of video memory */
    VidBase = (volatile unsigned long *)((unsigned char *)p9000VideoMem +
  				       0x200000L);
    /* Point It Past Non-P9000 Area Into Linear Control Region */
    CtlBase = (volatile unsigned long *)((unsigned char *)p9000VideoMem +
  				       0x100000L); 
  
  #ifdef DEBUG
    ErrorF("P9000 address space mapped to BASE: %lx  Vid: %lx  Ctl: %lx\n",
  	 (long)p9000VideoMem, (long)VidBase,
  	 (long)CtlBase);
  #endif
  }
  
  
  /*
   * p9000CleanUp --
   *    Disables the P9000 and restores things to text mode.
   */
  void
  p9000CleanUp()
  {
      /*disable video in the video controller */
      p9000Store(SRTCTL,CtlBase,0x01C4L); 
  
      /* Disable the P9000 and switch back to good old VGA */
      p9000VendorPtr->Disable();
  
      /* Restore the VT's LUT among other things */
      p9000RestoreVT();
  }
  
  /* 
   * p9000InitEnvironment --
   *    Initializes the P9000's drawing environment and clears the display.
   */
  void p9000InitEnvironment()
  {
      if (serverGeneration == 1)
      {
          /* Clear the display.  Need to set the color, origin, and size.
           * Then draw.
           */
        /* p9000ClearScreen(); */
      }
  
      /* Load the LUT */
      p9000InitLUT();
  }
  
  
  /* 
   * p9000ClearScreen --
   *     Clears video memory
   */
  void  p9000ClearScreen(void)
  {
    p9000NotBusy();         /* Wait for the P9000 to be free */
    /* Drawing a big black rectangle is probably a good way to clear things */
    if (p9000CRTCRegs.BytesPerPixel == 1)
      p9000Store(FGROUND, CtlBase, 0); /* 8 bit */
    else
      p9000Store(FGROUND, CtlBase, 0); /* 16 and 32 bit */
    p9000Store(RASTER, CtlBase, IGM_F_MASK);
    p9000Store(META_COORD | MC_QUAD | MC_YX, CtlBase,
  	     YX_PACK(0,0));
    p9000Store(META_COORD | MC_QUAD | MC_YX, CtlBase,
  	     YX_PACK((p9000CRTCRegs.XSize)*p9000CRTCRegs.BytesPerPixel,0));
    p9000Store(META_COORD | MC_QUAD | MC_YX, CtlBase,
  	     YX_PACK((p9000CRTCRegs.XSize)*p9000CRTCRegs.BytesPerPixel,
  		     p9000CRTCRegs.YSize));
    p9000Store(META_COORD | MC_QUAD | MC_YX, CtlBase,
  	     YX_PACK(0, p9000CRTCRegs.YSize));
    while (p9000Fetch(CMD_QUAD, CtlBase) & SR_ISSUE_QBN) ;
    /* Wait for the engine to be free again */
    p9000QBNotBusy();
    return;
  }
  
  
  /*
   * p9000ProbeMemConfig --
   *     Runs tests on the memory to determine which memory configuration
   *     is being used on the video card
   */
  /* NOTE: This has not been well tested since I only have a 2MB card
   * and haven't found any people with 1 meg cards... */
  void p9000ProbeMemConfig()
  {
  
  #ifdef DO_BROKEN_P9000_MEM_PROBE  /* This doesn't work, apparently */
    unsigned long memctr;
  
    /* Assume that we have 2 megs.  If this is false, errors
     * will occur and we can detect the type of memory
     * by looking at the errors.
     */
    p9000Store(MEM_CONFIG, CtlBase, 0x2L);
  
    /* Write something into a non-first row */
    p9000Store(0x100000, VidBase, 0x5a5a5a5a);
  
    /* Load values into all the memory */
    for (memctr = 0L; memctr < 1024L; memctr++)
      p9000Store(memctr*4L, VidBase, ~(memctr*4L));
  
    /* If there's an error in the first 32 longwords, 
     * we must have one bank of 8 256Kx4 VRAM's.
     * This is 1MB. */
    for (memctr = 0L; memctr < 32L; memctr++)
      if (p9000Fetch(memctr*4L, VidBase) != ~(memctr*4L))
        {
  	p9000MiscReg.memconfig = 0x0L;
  	p9000MiscReg.memsize = 0x100000L;
  	p9000Store(MEM_CONFIG, CtlBase, p9000MiscReg.memconfig);
  	return;
        }
  
    /* If there's an error after the 512th longword, 
     * we must have two banks of 8 128Kx8 VRAM's.
     * This is 1MB. */
    for (memctr = 32L; memctr < 1024L; memctr++)
      if (p9000Fetch(memctr*4L, VidBase) != ~(memctr*4L))
        {
  	p9000MiscReg.memconfig = 0x1L;
  	p9000MiscReg.memsize = 0x100000L;
  	p9000Store(MEM_CONFIG, CtlBase, p9000MiscReg.memconfig);
  	return;
        }
  
    /* Otherwise we guessed right and really do have 2MB of VRAM. 
     * (unless probe failed and we're overridden) */
    if (p9000InfoRec.videoRam == 1024) /* Allow user to override */
      {
        if (OFLG_ISSET(OPTION_VRAM_128, &p9000InfoRec.options))
  	p9000MiscReg.memconfig = 0x1L;  /* 128Kx8 VRAM */
        else  
  	p9000MiscReg.memconfig = 0x0L;  /* 256Kx4 VRAM */
        p9000MiscReg.memsize = 0x100000L;
        p9000Store(MEM_CONFIG, CtlBase, p9000MiscReg.memconfig);      
      }
    else  /* Go with the probed result */
      {
        p9000MiscReg.memconfig = 0x2L;
        p9000MiscReg.memsize = 0x200000L;
        p9000Store(MEM_CONFIG, CtlBase, p9000MiscReg.memconfig);
      }
  #else /* not DO_BROKEN_P9000_MEM_PROBE */
  
    /* Set the memory parameters as explicitly requested */
    if (p9000InfoRec.videoRam == 1024) /* Allow user to override */
      {
        if (OFLG_ISSET(OPTION_VRAM_128, &p9000InfoRec.options))
  	p9000MiscReg.memconfig = 0x1L;  /* 128Kx8 VRAM */
        else  
  	p9000MiscReg.memconfig = 0x0L;  /* 256Kx4 VRAM */
        p9000MiscReg.memsize = 0x100000L;
      }
    else  /* Go with the probed result */
      {
        p9000MiscReg.memconfig = 0x2L;
        p9000MiscReg.memsize = 0x200000L;
      }
    p9000Store(MEM_CONFIG, CtlBase, p9000MiscReg.memconfig);      
  #endif
  
  }
  