*BSD News Article 11200


Return to BSD News archive

Received: by minnie.vk1xwt.ampr.org with NNTP
	id AA1382 ; Tue, 23 Feb 93 14:38:35 EST
Path: sserve!manuel.anu.edu.au!munnari.oz.au!sgiblab!darwin.sura.net!mojo.eng.umd.edu!floyd
From: floyd@eng.umd.edu (Mike Tierney)
Newsgroups: comp.unix.bsd
Subject: driver mods for Diamond 24x (WD90C31)
Date: 15 Feb 1993 05:52:56 GMT
Organization: University of Maryland
Lines: 545
Sender: floyd@ni.umd.edu
Message-ID: <1lnb3oINNf0e@mojo.eng.umd.edu>
NNTP-Posting-Host: pepsi.eng.umd.edu
Keywords: Diamond 24X X386


I realize support for Diamond products has caused quite a controversy on the
net.  My intention in posting this code is not to suggest that people buy
Diamond boards to run X386.  I think if I had waited a little bit longer I
would not have bought the 24X board.  However, at the time it was one of the
only fast/cheap boards that would display 24-bit color.  This code is meant
for those people, who like me, are stuck with this board.

That said, I think the minor modifications made to the PVGA driver code
could be included in the X release to support those of us with these graphics
cards.  Since I have no idea who decides to include and test code like this
I leave it up to the net.

Without this code I could never get a sync'ed picture no matter what my
Xconfig dot clock values were set to.  Here is an excerpt from my current
Xconfig.  Yours may differ, but I think the frequency used must go in the
4th entry under clocks so that the clock index register is set correctly.
.
.
Clocks 25 28 0 72 0 0 0 0
.
.
"1024x768"    72    1024 1048 1184 1328    768 772 778 806
.


The code patches up the driver to add settings for a few of the extra WD90C31
registers.  It will NOT automatically set up the dot clocks.  This must be
done by booting DOS (YUCK) and setting the mode for the resolution you wish
to display.  Then, warm boot to UNIX and start up X.  I have been using this
code for quite some time under 386bsd and have had no problems.  There are
several lines commented out which set certain registers.  They do not need
to be set as far as I have seen, but I included them for completeness sake.

The driver.c routine in the mit/server/ddx/x386/vga256/drivers/pvga1
directory should be replaced with the code.

I do not know if the code fixes/breaks any other cards based on the Paradise
VGA chipset.  Any comments are welcome.  This code is provided without
any warranty of any kind.  Use it at your own risk.

I hope this code helps someone out.
Maybe someone will post dot clock code (Batman's was close but no cigar :-) )

Enough rambling,
   Please send any comments/questions to me at
     floyd@pepsi.eng.umd.edu
     Mike Tierney


-------- Begin included Code --------

/*
 * $Header: /home/x_cvs/mit/server/ddx/x386/vga256/drivers/pvga1/driver.c,v 1.9 1992/09/11 13:37:05 dawes Exp $
 * $XConsortium: driver.c,v 1.2 91/08/20 15:13:33 gildea Exp $
 *
 * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Thomas Roell not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Thomas Roell makes no representations
 * about the suitability of this software for any purpose.  It is provided
 * "as is" without express or implied warranty.
 *
 * THOMAS ROELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THOMAS ROELL 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.
 *
 * Author:  Thomas Roell, roell@informatik.tu-muenchen.de
 */

#include "X.h"
#include "input.h"
#include "screenint.h"

#include "compiler.h"

#include "x386.h"
#include "x386Priv.h"
#include "x386OSD.h"
#include "vga.h"

extern Bool x386Exiting;

typedef struct {
  vgaHWRec      std;    /* std IBM VGA register */
  unsigned char PR0A;   /* PVGA1A, WD90Cxx *//* 3CF, 09 */
  unsigned char PR0B;                        /* 3CF, 0A */
  unsigned char MemorySize;                  /* 3CF, 0B */
  unsigned char VideoSelect;                 /* 3CF, 0C */
  unsigned char CRTCCtrl;                    /* 3CF, 0D */
  unsigned char VideoCtrl;                   /* 3CF, 0E */
  unsigned char InterlaceStart; /* WD90Cxx *//* 3?5, 2C */
  unsigned char InterlaceEnd;                /* 3?5, 2D */
  unsigned char MiscCtrl1;                   /* 3?5, 2E */
  unsigned char MiscCtrl2;                   /* 3?5, 2F */
  unsigned char MiscCtrl3;                   /* 3?5, 30 */
  unsigned char VertTimeOverflow;            /* 3?5, 3E */
  unsigned char MemoryInterface;             /* 3C5, 10 */
  unsigned char InterfaceCtrl;  /* WD90C1x *//* 3C5, 11 */
  unsigned char MiscCtrl4;                   /* 3C5, 12 */
  unsigned char DramTiming;                  /* 3C5, 13 */
  unsigned char MemoryMap;                   /* 3C5, 14 */
} vgaPVGA1Rec, *vgaPVGA1Ptr;

static Bool  PVGA1Probe();
static char *PVGA1Ident();
static void  PVGA1ClockSelect();
static void  PVGA1EnterLeave();
static void  PVGA1Init();
static void *PVGA1Save();
static void  PVGA1Restore();
static void  PVGA1Adjust();
#ifdef MONOVGA
extern void  NoopDDA();
#else
extern void  PVGA1SetRead();
extern void  PVGA1SetWrite();
extern void  PVGA1SetReadWrite();
#endif


vgaVideoChipRec PVGA1 = {
  PVGA1Probe,
  PVGA1Ident,
  PVGA1EnterLeave,
  PVGA1Init,
  PVGA1Save,
  PVGA1Restore,
  PVGA1Adjust,
#ifdef MONOVGA
  NoopDDA,
  NoopDDA,
  NoopDDA,
#else
  PVGA1SetRead,
  PVGA1SetWrite,
  PVGA1SetReadWrite,
#endif
  0x10000,
  0x08000,
  15,
  0x7FFF,
  0x08000, 0x10000,
  0x00000, 0x08000,
  TRUE                                     /* Uses 2 banks */
};

#define new ((vgaPVGA1Ptr)vgaNewVideoState)


/*
 * PVGA1Ident
 */

static char *
PVGA1Ident()
{
  return("pvga1");
}


/*
 * PVGA1ClockSelect --
 *      select one of the possible clocks ...
 */

static void
PVGA1ClockSelect(no)
     int no;
{
  unsigned char temp;

  temp = inb(0x3CC);
  outb(0x3C2, ( temp & 0xf3) | ((no << 2) & 0x0C));
  outw(0x3CE, 0x0C | ((no & 0x04) << 7));
}



/*
 * PVGA1Probe --
 *      check up whether a PVGA1 based board is installed
 */

static Bool
PVGA1Probe()
{
  if (vga256InfoRec.chipset)
    {
      if (strcmp(vga256InfoRec.chipset, PVGA1Ident()))
	return (FALSE);
    }
  else

#ifdef MACH386
#define C 0xc0000
#define S 0x20000
    {
	char ident[4];
	int screen_addr;
	int fd;
	int ret;

	if ((fd = open("/dev/iopl", O_RDWR, 0)) < 0) {
		ErrorF("Failed to open /dev/iopl\n");
		return FALSE;
	}
#define KERN_SUCESS 0
	if (KERN_SUCESS != vm_allocate(task_self(), &screen_addr, S, TRUE)) {
		ErrorF("Failed vmallocate %x\n", S);
		close(fd);
		return FALSE;
	}
	if (mmap(screen_addr, S, 3, 1, fd, C) < 0) {
		ErrorF("Failed to mmap %x at %x\n", S, C);
		vm_deallocate(task_self(), screen_addr, S);
		close(fd);
		return FALSE;
	}
	*(int *)ident = *(int *)(screen_addr + 0x7D);
	if (KERN_SUCESS != vm_deallocate(task_self(), screen_addr, S)) {
		ErrorF("Failed vmdeallocate %x\n", S);
		close(fd);
		return FALSE;
	}
	if (strncmp(ident, "VGA=",4)) {
		close(fd);
		return(FALSE);
	}
	close(fd);
    }
#undef C
#undef S
#else /* MACH386 */
    {
      char ident[4];
      int  fd;

      if ((fd = open("/dev/mem", O_RDONLY)) == -1 ||
	  lseek(fd, 0xC007D, SEEK_SET) == -1 ||
	  read(fd, ident, 4) != 4)
	{
	  close(fd);
	  ErrorF("Failed to read VGA Bios.  (X386 must be installed as suid root.)\n");
	  return(FALSE);
	}
      
      close(fd);
      if (strncmp(ident, "VGA=",4)) return(FALSE);
    }
#endif /* MACH386 */

  PVGA1EnterLeave(ENTER);

  /*
   * Detect how much memory is installed
   */
  if (!vga256InfoRec.videoRam)
    {
      unsigned char config;

      outb(0x3CE, 0x0B); config = inb(0x3CF);
      
      switch(config & 0xC0) {
      case 0x00:
      case 0x40:
	vga256InfoRec.videoRam = 256;
	break;
      case 0x80:
	vga256InfoRec.videoRam = 512;
	break;
      case 0xC0:
	vga256InfoRec.videoRam = 1024;
	break;
      }
    }

  if (!vga256InfoRec.clocks) vgaGetClocks(8, PVGA1ClockSelect);

  vga256InfoRec.chipset = PVGA1Ident();
  return(TRUE);
}



/*
 * PVGA1EnterLeave --
 *      enable/disable io-mapping
 */

static void 
PVGA1EnterLeave(enter)
     Bool enter;
{
  unsigned char temp;

  if (enter)
    {
#ifdef HAS_USL_VTS
      ioctl(x386Info.consoleFd, KDENABIO, 0);
#endif

      vgaIOBase = (inb(0x3CC) & 0x01) ? 0x3D0 : 0x3B0;
      outw(0x3CE, 0x050F);           /* unlock PVGA1 Register Bank 1 */
      outw(vgaIOBase + 4, 0x8529);   /* unlock PVGA1 Register Bank 2 */
      outb(vgaIOBase + 4, 0x11); temp = inb(vgaIOBase + 5);
      outb(vgaIOBase + 5, temp & 0x7F);
    }
  else
    {
#ifdef HAS_USL_VTS
      ioctl(x386Info.consoleFd, KDDISABIO, 0);
#endif
    }
}



/*
 * PVGA1Restore --
 *      restore a video mode
 */

static void
PVGA1Restore(restore)
     vgaPVGA1Ptr restore;
{
  unsigned char temp;
   
  /*
   * First unlock all these special registers ...
   * NOTE: Locking will not be fully renabled !!!
   */
  outb(0x3CE, 0x0D); temp = inb(0x3CF); outb(0x3CF, temp & 0x1C);
  outb(0x3CE, 0x0E); temp = inb(0x3CF); outb(0x3CF, temp & 0xFB);

  outb(vgaIOBase + 4, 0x2A); temp = inb(vgaIOBase + 5);
  outb(vgaIOBase + 5, temp & 0xF8);

/*** MJT ***/
  outb(0x3C4, 0x06); temp = inb(0x3C5);  /* unlock sequencer extended regs. */
  outb(0x3C5, (temp | 0x48));

#ifndef MONOVGA
  outw(0x3CE, 0x0009);   /* segment select A */
  outw(0x3CE, 0x000A);   /* segment select B */
#endif

  vgaHWRestore(restore);

#ifndef MONOVGA
  outw(0x3CE, (restore->PR0A << 8) | 0x09);
  outw(0x3CE, (restore->PR0B << 8) | 0x0A);

  outb(0x3CE, 0x0B); temp = inb(0x3CF);          /* banking mode ... */
  outb(0x3CF, (temp & 0xF7) | (restore->MemorySize & 0x08));
#endif
       
  outw(0x3CE, (restore->VideoSelect << 8) | 0x0C);
#ifndef MONOVGA
  outw(0x3CE, (restore->CRTCCtrl << 8)    | 0x0D);
  outw(0x3CE, (restore->VideoCtrl << 8)   | 0x0E);
#endif
  
  /*
   * Now the WD90Cxx specials (Register Bank 2)
   */
  outw(vgaIOBase + 4, (restore->InterlaceStart << 8) | 0x2C);
  outw(vgaIOBase + 4, (restore->InterlaceEnd << 8)   | 0x2D);
  outw(vgaIOBase + 4, (restore->MiscCtrl2 << 8)      | 0x2F);

/** MJT **/
  outw(0x3C4, (restore->MemoryInterface << 8) | 0x10);
  outw(vgaIOBase + 4, (restore->MiscCtrl1 << 8)        | 0x2E);
/***
  outw(vgaIOBase + 4, (restore->MiscCtrl3 << 8)        | 0x30);
  outw(vgaIOBase + 4, (restore->VertTimeOverflow << 8) | 0x3E);
  outw(0x3C4, (restore->MemoryMap <<8) | 0x14);
  outw(0x3C4, (restore->MiscCtrl4 << 8) | 0x12);
  outw(0x3C4, (restore->DramTiming << 8) | 0x13);
***/

  /*
   * For the WD90C10 & WD90C11 we have to select segment mapping.
   * NOTE: Only bit7 is save/restored !!!!
   */
  outb(0x3C4, 0x11); temp = inb(0x3C5);
  outb(0x3C5, (temp & 0x7F) | (restore->InterfaceCtrl & 0x80));

  outw(0x3C4, 0x0300); /* now reenable the timing sequencer */
}



/*
 * PVGA1Save --
 *      save the current video mode
 */

static void *
PVGA1Save(save)
     vgaPVGA1Ptr save;
{
  unsigned char PR0A, PR0B, temp;

  vgaIOBase = (inb(0x3CC) & 0x01) ? 0x3D0 : 0x3B0;
  outb(0x3CE, 0x0D); temp = inb(0x3CF); outb(0x3CF, temp & 0x1C);
  outb(0x3CE, 0x0E); temp = inb(0x3CF); outb(0x3CF, temp & 0xFD);

  outb(vgaIOBase + 4, 0x2A); temp = inb(vgaIOBase + 5);
  outb(vgaIOBase + 5, temp & 0xF8);

#ifndef MONOVGA
  outb(0X3CE, 0x09); PR0A = inb(0x3CF); outb(0x3CF, 0x00);
  outb(0X3CE, 0x0A); PR0B = inb(0x3CF); outb(0x3CF, 0x00);
#endif

  save = (vgaPVGA1Ptr)vgaHWSave(save, sizeof(vgaPVGA1Rec));

#ifndef MONOVGA
  save->PR0A = PR0A;
  save->PR0B = PR0B;
  outb(0x3CE, 0x0B); save->MemorySize  = inb(0x3CF);
#endif
  outb(0x3CE, 0x0C); save->VideoSelect = inb(0x3CF);
#ifndef MONOVGA
  outb(0x3CE, 0x0D); save->CRTCCtrl    = inb(0x3CF);
  outb(0x3CE, 0x0E); save->VideoCtrl   = inb(0x3CF);
#endif

  /* WD90Cxx */
  outb(vgaIOBase + 4, 0x2C); save->InterlaceStart = inb(vgaIOBase+5);
  outb(vgaIOBase + 4, 0x2D); save->InterlaceEnd   = inb(vgaIOBase+5);
  outb(vgaIOBase + 4, 0x2F); save->MiscCtrl2      = inb(vgaIOBase+5);

/** MJT **/
  outb(vgaIOBase + 4, 0x2E); save->MiscCtrl1        = inb(vgaIOBase+5);
  outb(vgaIOBase + 4, 0x30); save->MiscCtrl3        = inb(vgaIOBase+5);
  outb(vgaIOBase + 4, 0x3E); save->VertTimeOverflow = inb(vgaIOBase+5);
  outb(0x3C4, 0x10); save->MemoryInterface = inb(0x3C5);
  outb(0x3C4, 0x12); save->MiscCtrl4       = inb(0x3C5);
  outb(0x3C4, 0x13); save->DramTiming      = inb(0x3C5);
  outb(0x3C4, 0x14); save->MemoryMap       = inb(0x3C5);

  /* WD90C1x */
  outb(0x3C4, 0x11); save->InterfaceCtrl = inb(0x3C5);

  return ((void *) save);
}



/*
 * PVGA1Init --
 *      Handle the initialization, etc. of a screen.
 */

static void
PVGA1Init(mode)
     DisplayModePtr mode;
{
  vgaHWInit(mode,sizeof(vgaPVGA1Rec));

#ifndef MONOVGA
  new->std.CRTC[19] = vga256InfoRec.virtualX >> 3; /* we are in byte-mode */
  new->std.CRTC[20] = 0x40;
  new->std.CRTC[23] = 0xE3; /* thats what the man says */
/*** MJT ***/
  new->std.CRTC[24] = 0xFF; /* internal scan-line cntr cleared when xxFF */

  new->PR0A = 0x00;
  new->PR0B = 0x00;
  new->MemorySize = 0x08;
#endif
  new->VideoSelect = (new->std.NoClock & 0x4) ? 0x02 : 0x00;
#ifndef MONOVGA
  new->CRTCCtrl = 0x00;
  new->VideoCtrl = 0x01;
#endif

  /* WD90Cxx */
  if (mode->Flags & V_INTERLACE) 
    {
      new->InterlaceStart = (mode->HSyncStart >> 3) - (mode->HTotal >> 4);
      new->InterlaceEnd = 0x20 | 
	((mode->HSyncEnd >> 3) - (mode->HTotal >> 4)) & 0x1F;
    }
  else
    {
      new->InterlaceStart = 0x00;
      new->InterlaceEnd = 0x00;
    }
  new->MiscCtrl2 = 0x00;

/*** MJT **/
  new->MiscCtrl1 = 0x60;         /* allow 256 color mode */
  new->MiscCtrl3 = 0x00;         /* normal clock 8 bit pixels not 16 */
  new->VertTimeOverflow = 0x00;  /* no vertical overflow */
  new->MemoryInterface = 0xC2;   /* set FIFO to req. mem when 2 levels empty */
  new->MiscCtrl4 = 0x04;         /* enable USR0 function */
  new->DramTiming = 0x80;
  new->MemoryMap = 0x10;         /* no virtual memory mapping */

  /* WD90C1x */
  new->InterfaceCtrl = 0x7D;    /* enable CRTC,Seq.,Grap Cont. 16-bit I/O
                                 *        Enable write buffer
                                 *        CPU read IOCHRDY timing 40ns-1MCLK
                                 *        Normal Read/Write offset PR0-A/B
                                 *        Turbo mode for blanked lines
                                 *        Turbo mode for text
                                 */
}
	


/*
 * PVGA1Adjust --
 *      adjust the current video frame to display the mousecursor
 */

static void 
PVGA1Adjust(x, y)
     int x, y;
{
#ifdef MONOVGA
  int           Base = (y * vga256InfoRec.virtualX + x) >> 3;
#else
  int           Base = (y * vga256InfoRec.virtualX + x) >> 2;
#endif
  unsigned char temp;

  outw(vgaIOBase + 4, (Base & 0x00FF00) | 0x0C);
  outw(vgaIOBase + 4, ((Base & 0x00FF) << 8) | 0x0D);
  outb(0x3CE, 0x0D); temp=inb(0x3CF); 
  outb(0x3CF, ((Base & 0x030000) >> 13) | (temp & 0xE7));
}