3501 lines
86 KiB
C
3501 lines
86 KiB
C
/*
|
|
* Copyright 1990 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char sccsid[] = "@(#)vx.c 1.1 94/10/31 Copyright 1990 Sun Micro VIP ";
|
|
#endif
|
|
#ifndef lint
|
|
static char sccsid1[] = "@(#)vxseg.c 2.9 91/03/04 Copyr 1990 Sun Micro VIP";
|
|
#endif
|
|
#ifndef lint
|
|
static char sccsid2[] = "@(#)vxsubr.c 1.2 91/03/04 Copyr 1990 Sun Micro VIP";
|
|
#endif
|
|
|
|
#include <vx.h>
|
|
|
|
#include <win.h>
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/map.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/dir.h>
|
|
#include <sys/user.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/file.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/vmmac.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/debug.h>
|
|
#include <sys/kmem_alloc.h>
|
|
|
|
#include <machine/mmu.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/pte.h>
|
|
#include <machine/enable.h>
|
|
#include <machine/eeprom.h>
|
|
#include <machine/vm_hat.h>
|
|
#include <machine/psl.h>
|
|
|
|
#include <sundev/mbvar.h>
|
|
#include <sundev/vxioctl.h>
|
|
#include <sundev/vxseg.h>
|
|
#include <sundev/vxvar.h>
|
|
#include <sundev/p4reg.h>
|
|
#include <sundev/vxhmvec.h>
|
|
|
|
#include <sun/fbio.h>
|
|
#include <sun/seg_kmem.h>
|
|
|
|
#include <pixrect/pixrect.h>
|
|
#include <pixrect/pr_impl_util.h>
|
|
#include <pixrect/pr_planegroups.h>
|
|
#include <pixrect/cg6var.h>
|
|
|
|
#include <sundev/cg6reg.h>
|
|
#include <sundev/vxreg.h>
|
|
|
|
#include <vm/as.h>
|
|
#include <vm/seg.h>
|
|
|
|
#ifndef PGT_VME_D32
|
|
#define PGT_VME_D32 0xC000000 /* == VME_D32<<26 == 0x3<<26 */
|
|
#endif
|
|
|
|
/*
|
|
* cgvx stuff
|
|
*/
|
|
|
|
int cgvxdebug=0; /* MUST be initialized to get into data segment
|
|
* otherwise kernel file cannot be patched using adb
|
|
*/
|
|
|
|
#define CG6_ID_SHIFT 24
|
|
#define CG6_RES_MASK 0x0f
|
|
#define CG6_PROBESIZE (NBPG)
|
|
|
|
/* forward references */
|
|
static void cgvxPAC1000(),
|
|
cgvxASIC(),
|
|
cgvxFHCTHC(),
|
|
cgvxRAMDAC();
|
|
|
|
static void savefbc();
|
|
|
|
static void cgvxCMap_get(),
|
|
cgvxCMap_put();
|
|
|
|
static int segvx_timeout();
|
|
|
|
#define ByteRep(A) (((A)<<24)|((A)<<16)|((A)<<8)|(A))
|
|
#define ColorFull(A) ((unsigned int)(((A)<<16)|((A)<<8)|(A)))
|
|
#define ColorRed(A) ((unsigned int)(A))
|
|
#define ColorGren(A) ((unsigned int)((A)<<8))
|
|
#define ColorBlue(A) ((unsigned int)((A)<<16))
|
|
#define Upper(A) (((A)-2)<<16)
|
|
#define Lower(A) ((A)-2)
|
|
#define FBCRESET 0x8000
|
|
#define MODE68 0x0200
|
|
|
|
/*
|
|
* This flag indicates that we should use kcopy instead of copyin
|
|
* for cursor ioctls.
|
|
*/
|
|
int fbio_kcopy;
|
|
extern int copyin(), kcopy();
|
|
extern caddr_t getpages(); /* FIXME: check the type */
|
|
|
|
/*
|
|
* Direction flags for savefbc()
|
|
*/
|
|
#define DIR_FROM_FBC 0
|
|
#define DIR_TO_FBC 1
|
|
|
|
#ifdef sun3x
|
|
#define PGT_CG6 0
|
|
#endif
|
|
#ifdef sun3
|
|
#define PGT_CG6 PGT_OBMEM
|
|
#endif
|
|
#ifdef sun4
|
|
#define PGT_CG6 PGT_OBIO
|
|
#endif
|
|
|
|
struct mapping vx_addrs[MAP_NMAP] = {
|
|
{ CG6_VADDR_CMAP, VX_ADDR_CMAP, CG6_CMAP_SZ, 0 },
|
|
{ CG6_VADDR_FHCTHC, VX_ADDR_FHCTHC, CG6_FHCTHC_SZ, 0 },
|
|
{ CG6_VADDR_FBCTEC, VX_ADDR_FBC, CG6_FBCTEC_SZ, 0 },
|
|
{ CG6_VADDR_COLOR, VX_ADDR_COLOR, VX_FB_SZ, 0 },
|
|
{ VX_VADDR_VIDCSR, VX_ADDR_VIDCSR, VX_VIDCSR_SZ, 0 },
|
|
{ VX_VADDR_VIDPAC, VX_ADDR_VIDPAC, VX_VIDPAC_SZ, 0 },
|
|
{ VX_VADDR_VIDMIX, VX_ADDR_VIDMIX, VX_VIDMIX_SZ, 0 },
|
|
{ VX_VADDR_VMECTL, VX_ADDR_VMECTL, VX_VMECTL_SZ, 0 },
|
|
{ CG6_VADDR_ROM, VX_ADDR_ROM, CG6_ROM_SZ, 0 },
|
|
{ 0, 0, 0, 0 },
|
|
{ 0, 0, VX_VX_SZ, 0 },
|
|
};
|
|
|
|
/*
|
|
* A blank page for mapping CG4 overlay/enable planes
|
|
*/
|
|
caddr_t blankpage;
|
|
|
|
/*
|
|
* driver per-unit data
|
|
*/
|
|
struct vx_softc vx_softc[NVX];
|
|
|
|
/*
|
|
* default structure for FBIOGTYPE ioctl
|
|
*/
|
|
static struct fbtype vxtypedefault = {
|
|
/* type h w depth cms size */
|
|
FBTYPE_SUN4COLOR, 0, 0, 8, 256, 0
|
|
};
|
|
|
|
/*
|
|
* default structure for FBIOGATTR ioctl
|
|
*/
|
|
static struct fbgattr vxattrdefault = {
|
|
/* real_type owner */
|
|
FBTYPE_SUNFAST_COLOR, 0,
|
|
/* fbtype: type h w depth cms size */
|
|
{ FBTYPE_SUNFAST_COLOR, 0, 0, 8, 256, 0 },
|
|
/* fbsattr: flags emu_type dev_specific */
|
|
{ 0, FBTYPE_SUN4COLOR, { 0, 0, 0, 1 } },
|
|
/* emu_types */
|
|
{ FBTYPE_SUNFAST_COLOR, FBTYPE_SUN4COLOR, -1, -1},
|
|
};
|
|
|
|
/*
|
|
* handy macros
|
|
*/
|
|
#define BZERO(d,c) bzero((caddr_t) (d), (u_int) (c))
|
|
#define COPY(f,s,d,c) (f((caddr_t) (s), (caddr_t) (d), (u_int) (c)))
|
|
#define CG6BCOPY(s,d,c) COPY(bcopy,(s),(d),(c))
|
|
#define COPYIN(s,d,c) COPY(copyin,(s),(d),(c))
|
|
#define COPYOUT(s,d,c) COPY(copyout,(s),(d),(c))
|
|
|
|
/*
|
|
* enable/disable interrupt
|
|
*/
|
|
#define TEC_EN_VBLANK_IRQ 0x20
|
|
#define TEC_HCMISC_IRQBIT 0x10
|
|
|
|
/* position value to use to disable HW cursor */
|
|
#define CGVX_CURSOR_OFFPOS (0xffe0ffe0)
|
|
|
|
#define fbmapout(virt, size) \
|
|
mapout(&Usrptmap[btokmx((struct pte *) (virt))], (int) btoc((size)));
|
|
|
|
#define vx_pgmint_enable(softc) \
|
|
{ \
|
|
struct vx_vmectl *vmectl = \
|
|
(struct vx_vmectl *)softc->va[MAP_VMECTL]; \
|
|
vmectl->pgm_csr = 0x100 | softc->pvec; \
|
|
}
|
|
|
|
#define vx_pgmint_disable(softc) \
|
|
{ \
|
|
struct vx_vmectl *vmectl = \
|
|
(struct vx_vmectl *)softc->va[MAP_VMECTL]; \
|
|
vmectl->pgm_csr = 0; \
|
|
}
|
|
|
|
#define vx_vrtint_enable(softc) \
|
|
{ \
|
|
struct vx_vmectl *vmectl = \
|
|
(struct vx_vmectl *)softc->va[MAP_VMECTL]; \
|
|
int x; \
|
|
x = splr(pritospl(softc->pri)); \
|
|
vmectl->vrt_csr = 0x100 | softc->vvec; \
|
|
softc->thc->l_thc_hcmisc = \
|
|
(softc->thc->l_thc_hcmisc & ~THC_HCMISC_RESET) | TEC_EN_VBLANK_IRQ; \
|
|
(void)splx(x); \
|
|
}
|
|
|
|
#define vx_vrtint_disable(softc) \
|
|
{ \
|
|
struct vx_vmectl *vmectl = \
|
|
(struct vx_vmectl *)softc->va[MAP_VMECTL]; \
|
|
int x; \
|
|
x = splr(pritospl(softc->pri)); \
|
|
softc->thc->l_thc_hcmisc = \
|
|
(softc->thc->l_thc_hcmisc & ~(THC_HCMISC_RESET | TEC_EN_VBLANK_IRQ)) \
|
|
| TEC_HCMISC_IRQBIT; \
|
|
vmectl->vrt_csr = 0; \
|
|
(void)splx(x); \
|
|
}
|
|
|
|
/* check if color map update is pending */
|
|
#define cgvx_intr_pending(softc) \
|
|
((softc)->cmap_count || \
|
|
(softc)->omap_update || \
|
|
(softc)->intr)
|
|
|
|
#if NWIN > 0
|
|
|
|
/*
|
|
* SunWindows specific stuff
|
|
*/
|
|
|
|
static cgvx_mem_rop(), cgvx_putcolormap();
|
|
|
|
/*
|
|
* kernel pixrect ops vector
|
|
*/
|
|
struct pixrectops cgvx_ops = {
|
|
cgvx_mem_rop,
|
|
cgvx_putcolormap,
|
|
mem_putattributes,
|
|
};
|
|
|
|
#endif
|
|
|
|
/* defines needed for probing for VX's and MVX's
|
|
* and mapping space for high memory access
|
|
*/
|
|
|
|
/* extracted from jetmap.h */
|
|
#define SO_INC 0x100000 /* Semaphore Op address modifier for clear */
|
|
#define SO_CLEAR 0x200000 /* Semaphore Op address modifier for increment */
|
|
#define SO_DEC 0x300000 /* Semaphore Op address modifier for decrement */
|
|
#define SP_DPRAM 0x01000000
|
|
#define DPRAMSIZE 0x8000
|
|
|
|
/* extracted from vxio.h */
|
|
#define JPP_NOFF 0x400000
|
|
#define JPP_BOFF 0x2000000
|
|
#define JPP_OFFSET 0x4000000
|
|
|
|
/* extracted from sfbvme.h */
|
|
#define FB_AV_HSB 0x01c00000
|
|
#define FB_AV_QUAD1 0x00400000 /* quadrant 1 */
|
|
|
|
/* extracted from sfb.h */
|
|
#define FB_AV_VIDBT 0x01a80000 /* Brooktree RAMDACs */
|
|
|
|
#define HMEM_SIZE 0x2000
|
|
|
|
int vxprobe(/* int reg, int unit */);
|
|
int vxattach(/* struct mb_device *mb */ );
|
|
int vxintr(/* struct vx_softc *vx*/);
|
|
|
|
static struct vx_procinfo *vxgetproc(/* dev_t dev */);
|
|
|
|
struct mb_device *vxinfo[NVX];
|
|
struct mb_driver vxdriver = {
|
|
vxprobe, /* probe routine */
|
|
NULL, /* slave routine */
|
|
vxattach, /* attach routine */
|
|
NULL, /* go routine */
|
|
NULL, /* done routine */
|
|
NULL, /* poll routine */
|
|
4, /* amount of memory space needed */
|
|
"vx", /* device name */
|
|
vxinfo, /* backpointer to device structs */
|
|
NULL, /* name of controller */
|
|
NULL, /* backpointer to controller structs */
|
|
NULL, /* mainbus flags */
|
|
NULL /* interrupt routine linked list */
|
|
};
|
|
|
|
/* if we ever go over 32 bits we change these guys
|
|
*/
|
|
#define VXSET(x,n) ((x) |= (1 << (n)))
|
|
#define VXISSET(x,n) ((x) & (1 << (n)))
|
|
#define VXCLEAR(x,n) ((x) &= ~(1 << (n)))
|
|
|
|
/* mapvme2kadr allocates a page of kernel memory and maps it to the give vme adr
|
|
* returns the kernel address that was mapped in
|
|
*/
|
|
long *
|
|
mapvme2kadr(adr, size)
|
|
long *adr; /* where in VME space to map */
|
|
int size; /* number of bytes to map */
|
|
{
|
|
int kmx, pageval;
|
|
long *kadr;
|
|
int s;
|
|
|
|
|
|
/* recompute size with padding added for wasted space between nearest
|
|
* page boundary and the given address
|
|
*/
|
|
/* size += ((int)adr % PAGESIZE); */
|
|
|
|
/* get index of empty entry in kernel map of size size (page aligned) */
|
|
|
|
s = splhigh();
|
|
if ((kmx = rmalloc(kernelmap, (long)btopr(size))) == 0)
|
|
panic("vx: probe: out of kernelmap for devices");
|
|
(void)splx(s);
|
|
|
|
/* convert it to kernel virtual address */
|
|
kadr = (long *)kmxtob(kmx);
|
|
|
|
/* compute the physical page at the address */
|
|
pageval = btop(adr) | PGT_VME_D32;
|
|
|
|
/* map it to the allocated kernel mem
|
|
* ptob(btopr(size)) rounds up to nearest page sized chunk
|
|
*/
|
|
segkmem_mapin(&kseg, (addr_t)kadr, ptob(btopr(size)),
|
|
(u_int)PROT_READ | PROT_WRITE, (u_int)pageval, 0);
|
|
|
|
/* return pointer to allocated kmem, offset from page boundary
|
|
to proper adr */
|
|
return(kadr + (((int)adr % PAGESIZE) / sizeof(long)));
|
|
}
|
|
|
|
|
|
/* freekadr deallocates kernel space beginning at 'kadr' created by mapvme2kadr */
|
|
freekadr(kadr, size)
|
|
long *kadr;
|
|
int size;
|
|
{
|
|
int s;
|
|
|
|
/* free up the kernel memory and return */
|
|
segkmem_mapout(&kseg, (addr_t)ptob(btopr(kadr)), ptob(btopr(size)));
|
|
s = splhigh();
|
|
rmfree(kernelmap, (long)ptob(btopr(size)), (u_long)kadr);
|
|
(void)splx(s);
|
|
}
|
|
|
|
|
|
/* sematouch attempts to poke 'val' into 'adr'
|
|
* if the address exists, returns the value at the same location after 2 reads
|
|
* otherwise returns -1
|
|
*/
|
|
long
|
|
sematouch(adr, val)
|
|
long *adr, val;
|
|
{
|
|
long res;
|
|
|
|
/* if the address exists, put in a value and take one out */
|
|
if (! pokel(adr, val)) {
|
|
res = *adr; /* read twice to trigger semaphore change */
|
|
res = *adr;
|
|
}
|
|
|
|
/* otherwise, indicate nonexistent address */
|
|
else
|
|
res = -1;
|
|
|
|
return(res);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static
|
|
cgvxprobe(reg, unit)
|
|
caddr_t reg;
|
|
int unit;
|
|
{
|
|
register struct vx_softc *softc = getsoftc(unit);
|
|
register struct mapping *mp;
|
|
register caddr_t *va;
|
|
register int i;
|
|
int id;
|
|
u_long kmx, rmalloc();
|
|
long peekval;
|
|
u_int hat_getkpfnum();
|
|
register long *Addr;
|
|
int resolution;
|
|
int status;
|
|
caddr_t legoreg;
|
|
|
|
legoreg =
|
|
(caddr_t)mapvme2kadr(
|
|
(long *)(ptob(hat_getkpfnum((addr_t)reg) + btop(VX_LEGO))), 1);
|
|
/*
|
|
* Determine frame buffer resolution:
|
|
*/
|
|
#ifdef sun3
|
|
/* this is for SunOS 3.x which does not know hat_petkpfnum */
|
|
/* we should simply use fbgetpage in this case */
|
|
softc->baseaddr = 0xFF000000;
|
|
#else
|
|
softc->baseaddr = ptob((u_long) hat_getkpfnum((addr_t) reg));
|
|
#endif
|
|
|
|
mmu_getkpte((addr_t) legoreg, (struct pte *)&status);
|
|
|
|
#if defined(SUN4_110)
|
|
if (cpu == CPU_SUN4_110 && !(softc->baseaddr & 0xf0000000))
|
|
softc->baseaddr |= 0xf0000000;
|
|
#endif
|
|
|
|
for(i = 0; i< MAP_NMAP; i++) {
|
|
softc->addrs[i].vaddr = vx_addrs[i].vaddr;
|
|
softc->addrs[i].paddr = vx_addrs[i].paddr;
|
|
softc->addrs[i].size = vx_addrs[i].size;
|
|
softc->addrs[i].prot = vx_addrs[i].prot;
|
|
}
|
|
softc->addrs[MAP_VX].vaddr = (u_int)ptob(hat_getkpfnum((addr_t)reg));
|
|
softc->addrs[MAP_VX].paddr = 0;
|
|
|
|
id = 0xffdeadff;
|
|
if (peekl((long *)legoreg, (long *)&id) == -1) {
|
|
DEBUGprint (2, "vxprobe: FBC CONFIG read failed!\n",0,0);
|
|
return 0;
|
|
}
|
|
else {
|
|
DEBUGprint1 (2, "vxprobe: FBC CONFIG=%x\n", id);
|
|
}
|
|
|
|
resolution = (id >> CG6_ID_SHIFT) & CG6_RES_MASK;
|
|
DEBUGprint1 (2, "vxprobe: %d sensed on monitor cable.\n", resolution);
|
|
switch (resolution) {
|
|
case 1:
|
|
case 3:
|
|
case 5:
|
|
case 4: /* Possibly the 13W3-4BNC adapter */
|
|
break;
|
|
|
|
/* If "no monitor" or unsupported resolution, default to 1280x1024!! */
|
|
case 2:
|
|
case 6:
|
|
case 7:
|
|
default:
|
|
printf ("UNSUPPORTED resolution sensed!\n");
|
|
resolution = 0;
|
|
case 0:
|
|
break;
|
|
}
|
|
softc->videofmt = vx_monitor[resolution];
|
|
softc->w = videofmt[softc->videofmt].x;
|
|
softc->h = videofmt[softc->videofmt].y;
|
|
softc->hz = videofmt[softc->videofmt].hz;
|
|
softc->clk = videofmt[softc->videofmt].clk;
|
|
|
|
fbmapin(legoreg, (int) VX_ADDR(VX_ADDR_CMAP), (int) NBPG);
|
|
|
|
/*
|
|
* Check for Brooktree RAMDACs; "0x4d" is BT461, "0x4c" is BT462.
|
|
*/
|
|
Addr = (long *)(legoreg + VX_BTID*4);
|
|
id = 0x80dead01;
|
|
status = peekl(Addr, (long *)&id);
|
|
if( !status ) {
|
|
int rd1=(id&0xff), rd2=((id>>8)&0xff), rd3=((id>>16)&0xff);
|
|
DEBUGprint1 (2, "vxprobe: RAMDAC ID = %x (", id&0xffffff);
|
|
DEBUGprint1 (2, "%x,", rd1);
|
|
DEBUGprint1 (2, "%x,", rd2);
|
|
DEBUGprint1 (2, "%x)\n", rd3);
|
|
if ( !(
|
|
(rd1==0x4c || rd1==0x4d) &&
|
|
(rd2==0x4c || rd2==0x4d) &&
|
|
(rd3==0x4c || rd3==0x4d)
|
|
) ) {
|
|
printf ("vxprobe: RAMDAC ID incorrect, = 0x%x!\n", id);
|
|
return 0;
|
|
}
|
|
}
|
|
else {
|
|
printf ("vxprobe: RAMDAC ID read failed!\n");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Map in each section
|
|
*/
|
|
for (va = softc->va,mp = vx_addrs, i = MAP_NPROBE; i; i--, mp++, va++) {
|
|
/*
|
|
* allocate enough pages
|
|
*/
|
|
kmx = rmalloc(kernelmap, (long) btoc(mp->size));
|
|
if (kmx == 0) {
|
|
va--, mp--, i++;
|
|
printf ("vxprobe: No kernel pages available!\n");
|
|
goto die;
|
|
}
|
|
|
|
/*
|
|
* get kernel virtual address
|
|
*/
|
|
*va = (caddr_t) kmxtob(kmx);
|
|
|
|
/*
|
|
* Map in this section
|
|
*/
|
|
fbmapin(*va, (int) VX_ADDR(mp->paddr), (int) mp->size);
|
|
|
|
/*
|
|
* Check if this section is present. If
|
|
* not, map out all sections, and return an error.
|
|
*/
|
|
if (mp->paddr!=VX_ADDR_COLOR && peekl((long *) *va, &peekval) == -1) {
|
|
printf ("vxprobe: Memory mapping failure va=%x pa=%x!\n",
|
|
va, mp->paddr);
|
|
die:
|
|
do {
|
|
fbmapout(*va, (int) mp->size);
|
|
va--;
|
|
mp--;
|
|
} while (i++ < MAP_NMAP);
|
|
freekadr((long *)legoreg, 1);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
softc->found = 1;
|
|
#ifdef NOTDEF
|
|
freekadr(reg, 1);
|
|
#endif
|
|
return (CG6_PROBESIZE);
|
|
}
|
|
|
|
vxprobe(reg, unit)
|
|
caddr_t reg;
|
|
int unit;
|
|
{
|
|
int bd, nd;
|
|
int dpbase, vxbase = ptob(hat_getkpfnum((addr_t)reg));
|
|
long *kadr;
|
|
register struct vx_softc *softc = getsoftc(unit);
|
|
|
|
/*
|
|
* Is there an SFB attached?
|
|
*/
|
|
DEBUGprint1 (2, "vxprobe: (0x%x) checking for VX...\n", vxbase);
|
|
if (cgvxprobe(reg, unit) == 0)
|
|
return(0);
|
|
else {
|
|
DEBUGprint1 (2, "vxprobe: identified VX on board 0\n", 0);
|
|
VXSET(softc->vx_ndmask, 0);
|
|
}
|
|
|
|
/*
|
|
* Test clear, inc and dec semaphores for presence of JPP's
|
|
*/
|
|
DEBUGprint1 (2, "vxprobe: checking for MVX's...\n", 0);
|
|
for (bd = 0; bd < (VX_MAXBOARDS - 1); bd++) {
|
|
for (nd = 0; nd < 4; nd++) {
|
|
dpbase = vxbase + JPP_OFFSET +
|
|
bd * JPP_BOFF + nd * JPP_NOFF + SP_DPRAM;
|
|
|
|
kadr = mapvme2kadr((long *)(dpbase + SO_INC), 1);
|
|
if (sematouch(kadr, (long)2) != 3)
|
|
break;
|
|
freekadr(kadr, 1);
|
|
|
|
kadr = mapvme2kadr((long *)(dpbase + SO_CLEAR), 1);
|
|
if (sematouch(kadr, (long)2) != 0)
|
|
break;
|
|
freekadr(kadr, 1);
|
|
|
|
kadr = mapvme2kadr((long *)(dpbase + SO_DEC), 1);
|
|
if (sematouch(kadr, (long)2) != 1)
|
|
break;
|
|
freekadr(kadr, 1);
|
|
}
|
|
|
|
/* if we're able to clear, inc, and dec all four nodes, we can assume
|
|
* a JPP is present, so set mask for all four at once
|
|
*/
|
|
if (nd == 4) {
|
|
DEBUGprint1 (2, "vxprobe: identified MVX on board %d\n", bd);
|
|
for (nd = 0; nd < 4; nd++)
|
|
VXSET(softc->vx_ndmask, (bd+1) * 4 + nd);
|
|
}
|
|
}
|
|
|
|
if (softc->vx_ndmask)
|
|
return 4;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
cgvxattach(md)
|
|
register struct mb_device *md;
|
|
{
|
|
register struct vx_softc *softc = getsoftc(md->md_unit);
|
|
register struct thc *thc;
|
|
register struct tec *tec;
|
|
register unsigned int *cfg, *asic, *pac, *video, *cmap;
|
|
u_int *cmd = ((u_int *)softc->va[MAP_CMAP]) + VX_BTOVRDMASK;
|
|
struct vx_vmectl *vmectl;
|
|
|
|
int bootdev;
|
|
struct vec *vp;
|
|
register unsigned int i;
|
|
|
|
DEBUGprint (0x1, "cgvxattach \n", 0, 0);
|
|
|
|
tec = (struct tec*) (softc->va[MAP_FBCTEC] + VX_TEC_POFF);
|
|
thc = (struct thc*) (softc->va[MAP_FHCTHC] + VX_THC_POFF);
|
|
cfg = (unsigned int *) (softc->va[MAP_FHCTHC]);
|
|
video = (unsigned int *) (softc->va[MAP_VIDCSR]);
|
|
pac = (unsigned int *) (softc->va[MAP_VIDPAC]);
|
|
asic = (unsigned int *) (softc->va[MAP_VIDMIX]);
|
|
cmap = (unsigned int *) (softc->va[MAP_CMAP]);
|
|
vmectl = (struct vx_vmectl *)softc->va[MAP_VMECTL];
|
|
|
|
if(!(*video & 1)) { /* PAC video controller not active.
|
|
* Starting it with any clock will allow
|
|
* access to the firmware version #. Stop
|
|
* it when we're done and let start it
|
|
* again when we configure it later. */
|
|
DEBUGprint1 (2, "vxattach: VX is secondary device.\n", 0);
|
|
bootdev=0;
|
|
*video |= 0x0001; /* Start PAC */
|
|
cgvx_pac_rel = (*pac & 0xff00) >> 8;
|
|
cgvx_pac_ver = (*pac & 0x00ff);
|
|
DEBUGprint (2,
|
|
"vxattach: VX video controller (PAC) s/w version %x.%x\n",
|
|
cgvx_pac_rel, cgvx_pac_ver);
|
|
*video &= 0xfe; /* Stop the PAC */
|
|
DELAY (1000); /* Wait sync to propagate to RAMDACs */
|
|
*video |= 0x80; /* Start Bt439 reset */
|
|
*video &= 0x7f; /* Stop Bt439 reset */
|
|
*(pac+1) = 0x0000; /* Turn off C/H sync + blanking */
|
|
*(pac+1) = 0x0e00; /* Turn on C/H sync + blanking */
|
|
} else { /* PAC video controller already active.
|
|
* Let's not mess with it and just get the
|
|
* firmware version. */
|
|
|
|
DEBUGprint1 (2, "vxattach: VX is boot device.\n", 0);
|
|
/* PAC running therefore assume we are boot device */
|
|
bootdev=1;
|
|
*video |= 0x20; /* Set PAC req. */
|
|
i = *(pac+4); /* Clear input FIFO */
|
|
while (*video & 0x8000)
|
|
i = *pac; /* Spin on PAC ack */
|
|
*(pac+30) = 0; /* Set PAC op to "Version#" */
|
|
i = *pac; /* Read any PAC reg to engage op */
|
|
VXDELAY ( (!(*video & 0x8000)), 100); /* Spin until PAC ack */
|
|
i = *pac; /* Read any version number */
|
|
cgvx_pac_rel = (i & 0xff00) >> 8;
|
|
cgvx_pac_ver = (i & 0x00ff);
|
|
DEBUGprint (2,
|
|
"vxattach: VX video controller (PAC) s/w version %x.%x\n",
|
|
cgvx_pac_rel, cgvx_pac_ver);
|
|
VXDELAY ( (*video & 0x8000), 100); /* Spin on PAC ack */
|
|
*video &= 0xdf; /* Clear PAC req. */
|
|
}
|
|
|
|
softc->thc = thc;
|
|
softc->owner = NULL;
|
|
|
|
vp = md->md_intr;
|
|
softc->pvec = vp->v_vec;
|
|
*(vp->v_vptr) = (int)softc;
|
|
vp++;
|
|
softc->vvec = vp->v_vec;
|
|
*(vp->v_vptr) = (int)softc;
|
|
vmectl->pgm_csr = 0;
|
|
vmectl->vrt_csr = 0;
|
|
|
|
softc->pri = md->md_intpri;
|
|
softc->size = VX_FB_SZ;
|
|
softc->intr = 0;
|
|
softc->gattr = vxattrdefault;
|
|
softc->gattr.fbtype.fb_height = softc->h;
|
|
softc->gattr.fbtype.fb_width = softc->w;
|
|
softc->gattr.fbtype.fb_size = softc->size;
|
|
|
|
DEBUGprint (2, "vxattach: ASIC..", 0,0);
|
|
cgvxASIC(asic);
|
|
DEBUGprint (2, "RAMDAC..", 0,0);
|
|
cgvxRAMDAC(cmap);
|
|
DEBUGprint (2, "FHCTHC..", 0,0);
|
|
cgvxFHCTHC(softc, cfg, thc);
|
|
tec->l_tec_mv = 0;
|
|
tec->l_tec_clip = 0;
|
|
tec->l_tec_vdc = 0;
|
|
|
|
/* Enable overlay plane display */
|
|
*cmd = 0x070707;
|
|
|
|
/* Turn off cursor, but do it myyyyyyyyyyyy way. JMP */
|
|
softc->thc->l_thc_cursor = CGVX_CURSOR_OFFPOS;
|
|
|
|
if (!bootdev) {
|
|
DEBUGprint (2, "VIDEO..", 0,0);
|
|
cgvxPAC1000(softc,video,pac);
|
|
}
|
|
|
|
/*
|
|
* initialize lockbase
|
|
*/
|
|
softc->lockbase = (caddr_t)
|
|
(softc->addrs[MAP_NMAP-1].vaddr +
|
|
softc->addrs[MAP_NMAP-1].size + PAGESIZE);
|
|
|
|
/*
|
|
* save fbc for PROM
|
|
*/
|
|
savefbc(&softc->fbc, (struct fbc*)(softc->va[MAP_FBCTEC]), DIR_FROM_FBC);
|
|
|
|
DEBUGprint (2,"\n", 0,0);
|
|
}
|
|
|
|
vxattach(md)
|
|
struct mb_device *md;
|
|
{
|
|
int nd;
|
|
struct vx_softc *vx = getsoftc(minor(md->md_unit));
|
|
struct vx_nodeinfo *vn = vx->vx_nodeinfo;
|
|
|
|
cgvxattach(md);
|
|
|
|
/* for each node under this device, map some kernel memory to the high page
|
|
* of the node's memory where important data is
|
|
*/
|
|
for (nd = 0; nd < VX_MAXNODES; nd++, vn++) {
|
|
if VXISSET(vx->vx_ndmask, nd) {
|
|
if (nd == 0)
|
|
vn->vn_datap = (int)mapvme2kadr(
|
|
(long *)(ptob(hat_getkpfnum(md->md_addr)) +
|
|
FB_AV_HSB + FB_AV_QUAD1 - HMEM_SIZE),
|
|
HMEM_SIZE);
|
|
else
|
|
vn->vn_datap = (int)mapvme2kadr(
|
|
(long *)(ptob(hat_getkpfnum(md->md_addr)) +
|
|
JPP_OFFSET + ((nd - 4) / 4) * JPP_BOFF +
|
|
((nd - 4) % 4) * JPP_NOFF +
|
|
SP_DPRAM + DPRAMSIZE - HMEM_SIZE),
|
|
HMEM_SIZE);
|
|
|
|
/* initialize the high memory vectors
|
|
* note: can only use 32 bit write/read requests
|
|
*/
|
|
*(unsigned int *)(vn->vn_datap + HPCVEC_OFFSET) = 0;
|
|
*(unsigned int *)(vn->vn_datap + HSRVEC_OFFSET) = HS_READY;
|
|
|
|
/* allocate space for the buffer used in DVMA transfers */
|
|
vn->vn_bp = (struct buf *)kmem_alloc(sizeof(struct buf));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Output configuration details.
|
|
*/
|
|
DEBUGprint1 (2, "vxattach: VX node mask = %x\n", vx->vx_ndmask);
|
|
{ int unit=minor(md->md_unit), mvx=0;
|
|
printf (" vx%d is", unit);
|
|
if VXISSET(vx->vx_ndmask, 0) {
|
|
printf (" a VX");
|
|
if (cgvx_pac_rel<=4) printf ("(rev1.1)");
|
|
else if (cgvx_pac_rel==5) printf ("(rev1.2)");
|
|
else if (cgvx_pac_rel==6) printf ("");
|
|
printf (" at %dx%d resolution", vx->w, vx->h);
|
|
DEBUGprint1 (2, " @ %dHz", vx->hz);
|
|
DEBUGprint1 (2, " (fmt=%d)", vx->videofmt);
|
|
}
|
|
for (nd=4; nd<VX_MAXNODES; nd+=4) if VXISSET(vx->vx_ndmask, nd) mvx++;
|
|
if (VXISSET(vx->vx_ndmask,0) & mvx) printf (", plus ");
|
|
if(mvx) printf ("%c MVX%c", (mvx>1)?(mvx+'0'):'a', (mvx>1)?'s':' ');
|
|
|
|
printf ("\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
cgvxmmap(dev, off, prot)
|
|
dev_t dev;
|
|
register off_t off;
|
|
int prot;
|
|
{
|
|
register struct vx_softc *softc = getsoftc(minor(dev));
|
|
register struct mapping *mp;
|
|
register int i;
|
|
|
|
/*
|
|
* See if the offset specifies a 'real' CG6 address
|
|
*/
|
|
for (mp = softc->addrs, i = 0; i < MAP_NMAP; i++, mp++) {
|
|
if (off >= (off_t) mp->vaddr && off < (off_t) (mp->vaddr + mp->size)) {
|
|
|
|
/*
|
|
* See if this section may only be mapped by root.
|
|
*/
|
|
if (mp->prot && !suser()) {
|
|
return (-1);
|
|
}
|
|
return (VX_ADDR(mp->paddr + (off - mp->vaddr)));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if this is within the CG4 Overlay/Enable/Color
|
|
* Frame buffer regions. Map overlay & enable to a page in
|
|
* the kernel.
|
|
*/
|
|
if (off < (off_t) softc->bw1size) {
|
|
if (blankpage == 0) {
|
|
blankpage = getpages(1, KMEM_SLEEP);
|
|
}
|
|
return (btop(blankpage));
|
|
}
|
|
|
|
return (-1);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
vxmmap(dev, off, prot)
|
|
dev_t dev;
|
|
off_t off;
|
|
int prot;
|
|
{
|
|
u_int addr;
|
|
|
|
addr = (u_int)hat_getkpfnum((addr_t)vxinfo[minor(dev)]->md_addr);
|
|
addr += btop(off);
|
|
|
|
return(addr);
|
|
}
|
|
|
|
|
|
/* vxhsr_service is called by vxintr to handle potential Host Segment Requests
|
|
* if there is a request, vxhsr_service unlocks any host pages which had been
|
|
* requested earlier and locks down the new page
|
|
* it then sets up a buffer structure for the DVMA transfer and then handles all
|
|
* the mapping using mbsetup()
|
|
*
|
|
* returns with hs_flag set to either HS_READY or HS_NOSPACE
|
|
* if the DVMA setup was successful, it returns READY along with hs_mapadr set
|
|
* to the VME address available to the vx node for DVMA transfers
|
|
*/
|
|
void
|
|
vxhsr_service(vx, vn)
|
|
struct vx_softc *vx;
|
|
struct vx_nodeinfo *vn;
|
|
{
|
|
struct hsrvec *hs = (struct hsrvec *)(vn->vn_datap + HSRVEC_OFFSET);
|
|
struct mb_device *md = vxinfo[minor(vx->vx_dev)];
|
|
struct vx_mseg *vms = &vn->vn_mseg;
|
|
struct vx_mseg *ovms = &vn->vn_omseg;
|
|
struct buf *bp = vn->vn_bp;
|
|
|
|
int fc, addr;
|
|
caddr_t a;
|
|
|
|
#ifdef notdef
|
|
printf("vxintr: vxhsr_service: hsrvec at kernel mem %x = %d\n", hs, hs->hs_flag);
|
|
#endif notdef
|
|
|
|
/* does the node have an hsr request? */
|
|
if (hs->hs_flag != HS_WANTPAGE)
|
|
return;
|
|
|
|
#ifdef notdef
|
|
printf("vxintr: vxhsr_service: phase 1\n");
|
|
printf("vxintr: vxhsr_service: a=%x s=%x p=%x\n",vms->vms_addr,vms->vms_size,vms->vms_pp);
|
|
printf("vxintr: vxhsr_service: a=%x s=%x p=%x\n",ovms->vms_addr,ovms->vms_size,ovms->vms_pp);
|
|
#endif notdef
|
|
|
|
/* if we had locked down a host page earlier,
|
|
* unlock it now and release the DVMA mapping
|
|
*/
|
|
if (ovms->vms_addr != NULL){
|
|
fc = (int)as_fault(ovms->vms_pp->p_as,ovms->vms_addr,PAGESIZE,F_SOFTUNLOCK,S_WRITE);
|
|
if (fc)
|
|
panic("vxhsr_service");
|
|
ovms->vms_addr = NULL;
|
|
mbrelse(md->md_hd, &vn->vn_mbinfo);
|
|
}
|
|
|
|
#ifdef notdef
|
|
printf("vxintr: vxhsr_service: phase 2, hs->hs_off=%x\n", hs->hs_off);
|
|
#endif notdef
|
|
|
|
/* compute the new virtual address offset from the exported segment's base */
|
|
a = vms->vms_addr + hs->hs_off;
|
|
|
|
/* make sure it's a valid address */
|
|
if (a > vms->vms_addr + vms->vms_size) {
|
|
printf("vxintr: page out of range off = %x\n", hs->hs_off);
|
|
hs->hs_flag = HS_READY;
|
|
return;
|
|
}
|
|
|
|
/* lock down the host page & store the address so we can unlock it later */
|
|
fc = (int)as_fault(vms->vms_pp->p_as,a,PAGESIZE,F_SOFTLOCK,S_WRITE);
|
|
if (fc)
|
|
panic("vxhsr_service");
|
|
ovms->vms_addr = a;
|
|
|
|
/* setup a buffer for the DVMA transfer */
|
|
bp->b_proc = vms->vms_pp;
|
|
bp->b_un.b_addr = a;
|
|
bp->b_dev = vx->vx_dev;
|
|
bp->b_bcount = PAGESIZE;
|
|
bp->b_blkno = 0;
|
|
bp->b_flags = B_BUSY | B_PHYS | B_WRITE;
|
|
|
|
#ifdef notdef
|
|
printf("vxintr: vxhsr_service: phase 3\n");
|
|
#endif notdef
|
|
|
|
/* try and setup a memory map for a DVMA transfer */
|
|
if (vn->vn_mbinfo = mbsetup(md->md_hd, bp, 1)) {
|
|
addr = MBI_ADDR(vn->vn_mbinfo); /* convert the cookie */
|
|
hs->hs_mapaddr = addr; /* let caller know where it is */
|
|
hs->hs_flag = HS_READY;
|
|
}
|
|
else
|
|
hs->hs_flag = HS_NOSPACE; /* indicate a map couldn't be found */
|
|
|
|
#ifdef notdef
|
|
printf("vxintr: vxhsr_service: fc=%x addr=%x\n", fc, addr);
|
|
#endif notdef
|
|
}
|
|
|
|
vxintr(vx)
|
|
struct vx_softc *vx;
|
|
{
|
|
struct vx_nodeinfo *vn = vx->vx_nodeinfo;
|
|
struct vx_procinfo *vp;
|
|
int nd, inproc;
|
|
|
|
/* TODO: on interrupt, shift ndmask and && with 1 as part of loop to avoid VXISSET
|
|
*/
|
|
|
|
if (vx->intr & VX_VRTWAIT) {
|
|
vx->intr &= ~VX_VRTWAIT;
|
|
vx_pgmint_disable(vx);
|
|
wakeup((caddr_t)vx);
|
|
return(0);
|
|
}
|
|
|
|
/* check each node to determine if it called for an interrupt */
|
|
for (nd = 0; nd < VX_MAXNODES; nd++, vn++) {
|
|
if (VXISSET(vx->vx_ndmask, nd)) {
|
|
|
|
#ifdef notdef
|
|
printf("vxintr: checking for hpc request\n");
|
|
#endif notdef
|
|
|
|
/* does the node have and hpc request? */
|
|
if (*(unsigned int *)(vn->vn_datap + HPCVEC_OFFSET)) {
|
|
/* yes, so check each process
|
|
* and find out if its interruptable for this node
|
|
*/
|
|
#ifdef notdef
|
|
printf("HPC flag set for node %d. Checking processes...\n", nd);
|
|
#endif notdef
|
|
vp = vx->vx_procinfo;
|
|
for (inproc = 0; inproc < VX_MAXPROCS; inproc++, vp++) {
|
|
if (vp->vp_procp && VXISSET(vp->vp_intrmask, nd)) {
|
|
/* yes, it is, so go ahead and send it and interrupt signal */
|
|
#ifdef notdef
|
|
printf("sending signal to %d\n", vp->vp_procp->p_pid);
|
|
#endif
|
|
psignal(vp->vp_procp, SIGIO);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef notdef
|
|
printf("vxintr: checking for hsr request\n");
|
|
#endif notdef
|
|
|
|
/* handle possible hsr request by this node */
|
|
vxhsr_service(vx, vn);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
#ifdef notdef
|
|
vxselect()
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
#endif
|
|
|
|
vxopen(dev, flag)
|
|
dev_t dev; int flag;
|
|
{
|
|
return fbopen(dev, flag, NVX, vxinfo);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
vxclose(dev, flag)
|
|
dev_t dev;
|
|
int flag;
|
|
{
|
|
register struct vx_softc *softc = getsoftc(minor(dev));
|
|
|
|
softc->owner = NULL;
|
|
|
|
softc->gattr = vxattrdefault;
|
|
softc->gattr.fbtype.fb_height = softc->h;
|
|
softc->gattr.fbtype.fb_width = softc->w;
|
|
softc->gattr.fbtype.fb_size = softc->size;
|
|
softc->cur.enable = 0;
|
|
cgvx_setcurpos(softc);
|
|
|
|
/*
|
|
* Restore fbc for PROM
|
|
*/
|
|
savefbc((struct fbc*)(softc->va[MAP_FBCTEC]), &softc->fbc, DIR_TO_FBC);
|
|
|
|
/* also, reprep the fhc/thc */
|
|
|
|
cgvxFHCTHC(softc, (unsigned int *) (softc->va[MAP_FHCTHC]),
|
|
(struct thc*) (softc->va[MAP_FHCTHC] + VX_THC_POFF));
|
|
}
|
|
|
|
/* XXX - we use vx_segfree to simulate a close per process rather
|
|
than a close on last process.
|
|
*/
|
|
int
|
|
vxfreethings(dev)
|
|
dev_t dev;
|
|
{
|
|
struct vx_softc *vx = getsoftc(minor(dev));
|
|
struct vx_nodeinfo *vn = vx->vx_nodeinfo;
|
|
struct vx_procinfo *vp;
|
|
struct vx_mseg *ovms;
|
|
int i, fc;
|
|
|
|
for(i = 0; i < VX_NKEYS; i++) {
|
|
if (vx->vx_keyprocs[i] == u.u_procp)
|
|
fc = vxunkey(vx, i);
|
|
}
|
|
|
|
if ((vp = vxgetproc(dev)) == NULL)
|
|
return(ENXIO);
|
|
|
|
/* XXX - if this is called by segfree it will unlock ALL nodes associated with
|
|
* the freeing process. We may only want to unlock a particular node!
|
|
* segfree really needs its own clean up code, so we only unlock one at a time
|
|
*/
|
|
for (i=0; i<VX_MAXNODES; i++,vn++){
|
|
if (VXISSET(vx->vx_ndmask, i)) {
|
|
if (VXISSET(vp->vp_lockmask, i)) {
|
|
fc = vxunlock(vn,vp,i);
|
|
}
|
|
|
|
/* if there's an old memory segment still connected for DMA, release it */
|
|
ovms = &vn->vn_omseg;
|
|
if (ovms->vms_addr != NULL){
|
|
fc = (int)as_fault(ovms->vms_pp->p_as,ovms->vms_addr,PAGESIZE,F_SOFTUNLOCK,S_WRITE);
|
|
if (fc)
|
|
panic("vxfreethings");
|
|
ovms->vms_addr = NULL;
|
|
mbrelse(vxinfo[minor(dev)]->md_hd, &vn->vn_mbinfo);
|
|
}
|
|
}
|
|
}
|
|
vp->vp_lockmask = 0;
|
|
vp->vp_procp = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct vx_procinfo *
|
|
vxgetproc(dev)
|
|
{
|
|
struct vx_softc *vx = getsoftc(minor(dev));
|
|
struct vx_procinfo *vp = vx->vx_procinfo;
|
|
int i;
|
|
|
|
for (i = 0; i < VX_MAXPROCS; i++, vp++) {
|
|
if (vp->vp_procp == u.u_procp)
|
|
return(vp);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static
|
|
cgvxioctl(dev, cmd, data, flag)
|
|
dev_t dev;
|
|
int cmd;
|
|
caddr_t data;
|
|
int flag;
|
|
{
|
|
register int unit = minor(dev);
|
|
register struct vx_softc *softc = getsoftc(unit);
|
|
int cursor_cmap;
|
|
int (*copyfun)();
|
|
extern int segvx_graballoc();
|
|
extern int segvx_grabattach();
|
|
extern int segvx_grabfree();
|
|
extern int segvx_grabinfo();
|
|
|
|
static union {
|
|
u_char cmap[3*CG6_CMAP_ENTRIES];
|
|
u_long cur[32];
|
|
} iobuf;
|
|
register struct thc *thc;
|
|
register unsigned int *cfg, *asic, *pac, *video;
|
|
|
|
DEBUGprint (0x1, "cgvxioctl %x\n", cmd, 0);
|
|
|
|
/* default to updating normal colormap */
|
|
cursor_cmap = 0;
|
|
|
|
/* figure out if kernel or user mode call */
|
|
copyfun = fbio_kcopy ? kcopy : copyin;
|
|
|
|
switch (cmd) {
|
|
|
|
case FBIOPUTCMAP:
|
|
case FBIOGETCMAP:
|
|
{
|
|
struct fbcmap *cmap;
|
|
u_int index;
|
|
u_int count;
|
|
u_char *map;
|
|
#ifdef notdef
|
|
u_int entries;
|
|
#endif
|
|
int error;
|
|
register u_int *colorlut = (u_int *)softc->va[MAP_CMAP];
|
|
static void cgvx_update_cmap();
|
|
|
|
cmap_ioctl:
|
|
cmap = (struct fbcmap *) data;
|
|
index = (u_int) cmap->index;
|
|
count = (u_int) cmap->count;
|
|
|
|
DEBUGprint (0x4, "cgvxioctl FBIOP/GCMAP idx=%d cnt=%d\n", index, count);
|
|
|
|
if (cmd == FBIOPUTCMAP) {
|
|
|
|
if (cgvx_intr_pending(softc)) {
|
|
if (!servicing_interrupt())
|
|
synctovrt(softc);
|
|
vx_vrtint_disable(softc);
|
|
}
|
|
|
|
if (cursor_cmap == 0)
|
|
switch (PIX_ATTRGROUP(index)) {
|
|
case 0:
|
|
case PIXPG_8BIT_COLOR:
|
|
map = softc->cmap_rgb;
|
|
#ifdef notdef
|
|
entries = CG6_CMAP_ENTRIES;
|
|
#endif
|
|
break;
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
else {
|
|
map = softc->omap_rgb;
|
|
#ifdef notdef
|
|
entries = 2;
|
|
#endif
|
|
}
|
|
|
|
do {
|
|
if (error = COPY(copyfun, cmap->red, iobuf.cmap, count))
|
|
break;
|
|
CG6BCOPY(iobuf.cmap, map, count);
|
|
|
|
if (error = COPY(copyfun, cmap->green, iobuf.cmap, count))
|
|
break;
|
|
CG6BCOPY(iobuf.cmap, map+count, count);
|
|
|
|
if (error = COPY(copyfun, cmap->blue, iobuf.cmap, count))
|
|
break;
|
|
CG6BCOPY(iobuf.cmap, map+2*count, count);
|
|
} while(_ZERO_);
|
|
|
|
if (error) {
|
|
if (cgvx_intr_pending(softc))
|
|
vx_vrtint_enable(softc);
|
|
return EFAULT;
|
|
}
|
|
|
|
if (cursor_cmap)
|
|
count = 0;
|
|
|
|
cgvx_update_cmap(softc, index, count);
|
|
vx_vrtint_enable(softc);
|
|
|
|
} else {
|
|
cgvxCMap_get (0, index, count, iobuf.cmap, colorlut);
|
|
(void)copyout ((caddr_t)iobuf.cmap, (caddr_t)cmap->red, count);
|
|
cgvxCMap_get (1, index, count, iobuf.cmap, colorlut);
|
|
(void)copyout ((caddr_t)iobuf.cmap, (caddr_t)cmap->green, count);
|
|
cgvxCMap_get (2, index, count, iobuf.cmap, colorlut);
|
|
(void)copyout ((caddr_t)iobuf.cmap, (caddr_t)cmap->blue, count);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FBIOSATTR:
|
|
{
|
|
register struct fbsattr *sattr = (struct fbsattr *) data;
|
|
|
|
#ifdef ONLY_OWNER_CAN_SATTR
|
|
/* this can only happen for the owner */
|
|
if (softc->owner != u.u_procp)
|
|
return (ENOTTY);
|
|
#endif ONLY_OWNER_CAN_SATTR
|
|
|
|
softc->gattr.sattr.flags = sattr->flags;
|
|
|
|
if (sattr->emu_type != -1)
|
|
softc->gattr.sattr.emu_type = sattr->emu_type;
|
|
|
|
if (sattr->flags & FB_ATTR_DEVSPECIFIC) {
|
|
bcopy((char *) sattr->dev_specific,
|
|
(char *) softc->gattr.sattr.dev_specific,
|
|
sizeof (sattr->dev_specific));
|
|
|
|
if (softc->gattr.sattr.dev_specific
|
|
[FB_ATTR_CG6_SETOWNER_CMD] == 1) {
|
|
struct proc *pfind();
|
|
register struct proc *newowner = 0;
|
|
|
|
if (softc->gattr.sattr.dev_specific
|
|
[FB_ATTR_CG6_SETOWNER_PID] > 0 &&
|
|
(newowner = pfind(
|
|
softc->gattr.sattr.dev_specific
|
|
[FB_ATTR_CG6_SETOWNER_PID]))) {
|
|
softc->owner = newowner;
|
|
softc->gattr.owner = newowner->p_pid;
|
|
}
|
|
|
|
softc->gattr.sattr.dev_specific
|
|
[FB_ATTR_CG6_SETOWNER_CMD] = 0;
|
|
softc->gattr.sattr.dev_specific
|
|
[FB_ATTR_CG6_SETOWNER_PID] = 0;
|
|
|
|
if (!newowner)
|
|
return (ESRCH);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case FBIOGATTR:
|
|
{
|
|
register struct fbgattr *gattr = (struct fbgattr *) data;
|
|
|
|
|
|
/* set owner if not owned or previous owner is dead */
|
|
if (softc->owner == NULL ||
|
|
softc->owner->p_stat == NULL ||
|
|
softc->owner->p_pid != softc->gattr.owner) {
|
|
|
|
softc->owner = u.u_procp;
|
|
softc->gattr.owner = u.u_procp->p_pid;
|
|
}
|
|
|
|
*gattr = softc->gattr;
|
|
|
|
if (u.u_procp == softc->owner) {
|
|
gattr->owner = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FBIOGTYPE:
|
|
{
|
|
register struct fbtype *fb = (struct fbtype *) data;
|
|
|
|
/* set owner if not owned or previous owner is dead */
|
|
if (softc->owner == NULL ||
|
|
softc->owner->p_stat == NULL ||
|
|
softc->owner->p_pid != softc->gattr.owner) {
|
|
|
|
softc->owner = u.u_procp;
|
|
softc->gattr.owner = u.u_procp->p_pid;
|
|
}
|
|
|
|
switch (softc->gattr.sattr.emu_type) {
|
|
case FBTYPE_SUN4COLOR:
|
|
*fb = vxtypedefault;
|
|
fb->fb_height = softc->h;
|
|
fb->fb_width = softc->w;
|
|
fb->fb_size = softc->size;
|
|
break;
|
|
|
|
case FBTYPE_SUNFAST_COLOR:
|
|
default:
|
|
*fb = softc->gattr.fbtype;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
#if NWIN > 0
|
|
|
|
case FBIOGPIXRECT:
|
|
{
|
|
((struct fbpixrect *) data)->fbpr_pixrect = &softc->pr;
|
|
|
|
softc->pr.pr_data = (caddr_t) &softc->prd;
|
|
|
|
/* initialize pixrect */
|
|
|
|
softc->pr.pr_ops = &cgvx_ops;
|
|
softc->pr.pr_size.x = softc->w;
|
|
softc->pr.pr_size.y = softc->h;
|
|
softc->pr.pr_depth = CG6_DEPTH;
|
|
|
|
/* initialize private data */
|
|
|
|
softc->prd.mpr.md_linebytes = mpr_linebytes(softc->w, CG6_DEPTH);
|
|
softc->prd.mpr.md_image = (short *) (softc->va[MAP_COLOR]);
|
|
softc->prd.mpr.md_offset.x = 0;
|
|
softc->prd.mpr.md_offset.y = 0;
|
|
|
|
/* Need unit number added to 'prd'!!! */
|
|
softc->prd.mpr.md_primary = unit;
|
|
|
|
softc->prd.mpr.md_flags = MP_DISPLAY | MP_PLANEMASK;
|
|
softc->prd.planes = 255;
|
|
|
|
/* enable video */
|
|
/*
|
|
setvideoenable(1); /* does this do anything for cg6 ? */
|
|
|
|
cgvx_videnable(softc, 1);
|
|
}
|
|
break;
|
|
|
|
#endif NWIN > 0
|
|
|
|
/* set video flags */
|
|
case FBIOSVIDEO:
|
|
{
|
|
register int *nvideo = (int *) data;
|
|
unsigned int *dac = (unsigned int*) (softc->va[MAP_CMAP]);
|
|
/*
|
|
* VX video never really goes "off". This just switches the
|
|
* RAMDACs to display to/from "overlay color 0" or the color
|
|
* palette. This requires that "overlay color 0" has previously
|
|
* been set to black!!
|
|
*/
|
|
if (*nvideo & FBVIDEO_ON) {
|
|
*(dac+VX_BTCR0) = 0x505050;
|
|
softc->cur.video_blanking = 0; /* Enable video */
|
|
softc->cur.enable = 1; /* Turn on cursor */
|
|
cgvx_setcurpos(softc); /* Do it */
|
|
}
|
|
else {
|
|
*(dac+VX_BTCR0) = 0x707070;
|
|
softc->cur.enable = 0; /* Turn off cursor */
|
|
cgvx_setcurpos(softc); /* Do it */
|
|
softc->cur.video_blanking = 1; /* Then disable video */
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* get video flags */
|
|
case FBIOGVIDEO:
|
|
{
|
|
unsigned int *dac = (unsigned int*) (softc->va[MAP_CMAP]);
|
|
*(int *)data = (*(dac+VX_BTCR0)==0x505050) ? FBVIDEO_ON : FBVIDEO_OFF;
|
|
}
|
|
break;
|
|
|
|
case GRABPAGEALLOC: /* Window Grabber */
|
|
return (segvx_graballoc(data, softc));
|
|
case GRABATTACH: /* Window Grabber */
|
|
return (segvx_grabattach(data));
|
|
case GRABPAGEFREE: /* Window Grabber */
|
|
return (segvx_grabfree(data, softc));
|
|
case GRABLOCKINFO: /* Window Grabber */
|
|
return (segvx_grabinfo(data));
|
|
|
|
/* HW cursor control */
|
|
case FBIOSCURSOR: {
|
|
struct fbcursor *cp = (struct fbcursor *) data;
|
|
int set = cp->set;
|
|
int cbytes;
|
|
|
|
if (set & FB_CUR_SETCUR)
|
|
softc->cur.enable = cp->enable;
|
|
|
|
if (set & FB_CUR_SETPOS)
|
|
softc->cur.pos = cp->pos;
|
|
|
|
if (set & FB_CUR_SETHOT)
|
|
softc->cur.hot = cp->hot;
|
|
|
|
/* update hardware */
|
|
cgvx_setcurpos(softc);
|
|
|
|
if (set & FB_CUR_SETSHAPE) {
|
|
if ((u_int) cp->size.x > 32 || (u_int) cp->size.y > 32)
|
|
return EINVAL;
|
|
softc->cur.size = cp->size;
|
|
|
|
/* compute cursor bitmap bytes */
|
|
cbytes = softc->cur.size.y * sizeof softc->cur.image[0];
|
|
|
|
/* copy cursor image into softc */
|
|
if (COPY(copyfun, cp->image, iobuf.cur, cbytes))
|
|
return EFAULT;
|
|
BZERO(softc->cur.image, sizeof softc->cur.image);
|
|
CG6BCOPY(iobuf.cur, softc->cur.image, cbytes);
|
|
if (COPY(copyfun, cp->mask, iobuf.cur, cbytes))
|
|
return EFAULT;
|
|
BZERO(softc->cur.mask, sizeof softc->cur.mask);
|
|
CG6BCOPY(iobuf.cur, softc->cur.mask, cbytes);
|
|
|
|
/* load into hardware */
|
|
cgvx_setcurshape(softc);
|
|
}
|
|
|
|
/* load colormap */
|
|
if (set & FB_CUR_SETCMAP) {
|
|
cursor_cmap = 1;
|
|
cmd = FBIOPUTCMAP;
|
|
data = (caddr_t) &cp->cmap;
|
|
goto cmap_ioctl;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case FBIOGCURSOR:
|
|
{
|
|
struct fbcursor *cp = (struct fbcursor *) data;
|
|
int cbytes;
|
|
|
|
cp->set = 0;
|
|
cp->enable = softc->cur.enable;
|
|
cp->pos = softc->cur.pos;
|
|
cp->hot = softc->cur.hot;
|
|
cp->size = softc->cur.size;
|
|
|
|
/* compute cursor bitmap bytes */
|
|
cbytes = softc->cur.size.y * sizeof softc->cur.image[0];
|
|
|
|
/* if image pointer is non-null copy both bitmaps */
|
|
if (cp->image &&
|
|
(COPYOUT(softc->cur.image, cp->image, cbytes) ||
|
|
COPYOUT(softc->cur.mask, cp->mask, cbytes)))
|
|
return EFAULT;
|
|
|
|
/* if red pointer is non-null, copy colormap */
|
|
if (cp->cmap.red) {
|
|
cursor_cmap = 1;
|
|
cmd = FBIOGETCMAP;
|
|
data = (caddr_t) &cp->cmap;
|
|
goto cmap_ioctl;
|
|
} else { /* just trying to find out colormap size */
|
|
cp->cmap.index = 0;
|
|
cp->cmap.count = 2;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FBIOSCURPOS:
|
|
softc->cur.pos = * (struct fbcurpos *) data;
|
|
cgvx_setcurpos(softc);
|
|
break;
|
|
|
|
case FBIOGCURPOS:
|
|
* (struct fbcurpos *) data = softc->cur.pos;
|
|
break;
|
|
|
|
case FBIOGCURMAX:
|
|
{
|
|
static struct fbcurpos curmax = { 32, 32 };
|
|
|
|
* (struct fbcurpos *) data = curmax;
|
|
break;
|
|
}
|
|
|
|
case FBRESET:
|
|
{
|
|
|
|
DEBUGprint (0x2, "vxioctl RESET\n", 0, 0);
|
|
thc = (struct thc*) (softc->va[MAP_FHCTHC]+VX_THC_POFF);
|
|
cfg = (unsigned int *) (softc->va[MAP_FHCTHC]);
|
|
video = (unsigned int *) (softc->va[MAP_VIDCSR]);
|
|
pac = (unsigned int *) (softc->va[MAP_VIDPAC]);
|
|
asic = (unsigned int *) (softc->va[MAP_VIDMIX]);
|
|
|
|
softc->w = softc->gattr.fbtype.fb_width =
|
|
videofmt[softc->videofmt].x;
|
|
softc->h = softc->gattr.fbtype.fb_height =
|
|
videofmt[softc->videofmt].y;
|
|
softc->clk = videofmt[softc->videofmt].clk;
|
|
softc->hz = videofmt[softc->videofmt].hz;
|
|
softc->gattr.fbtype.fb_size = softc->w * softc->h;
|
|
|
|
DEBUGprint (2, "vxioctl: Init ASIC..\n",0,0);
|
|
cgvxASIC(asic);
|
|
DEBUGprint (2, "FHCTHC..\n",0,0);
|
|
cgvxFHCTHC(softc,cfg, thc);
|
|
DEBUGprint (2, "VIDEO..\n",0,0);
|
|
cgvxPAC1000(softc,video,pac);
|
|
DEBUGprint (2, "FBRESET complete\n",0,0);
|
|
break;
|
|
}
|
|
|
|
case FBIOVERTICAL:
|
|
|
|
switch(*(int *)data) {
|
|
|
|
#ifdef notdef
|
|
case VX_VRTSTOP:
|
|
softc->intr = 0;
|
|
softc->owner = NULL;
|
|
softc->gattr.owner = 0;
|
|
vx_vrtint_disable(softc);
|
|
break;
|
|
|
|
case VX_VRTSTART:
|
|
softc->intr = VX_VRTSTART;
|
|
softc->owner = u.u_procp;
|
|
softc->gattr.owner = u.u_procp->p_pid;
|
|
vx_vrtint_enable(softc);
|
|
break;
|
|
#endif
|
|
|
|
case VX_VRTWAIT:
|
|
{
|
|
int nx;
|
|
|
|
#ifdef notdef
|
|
timeout(vxtout, softc, 2*hz);
|
|
#endif
|
|
nx = splr(pritospl(softc->pri));
|
|
softc->intr |= VX_VRTWAIT;
|
|
vx_vrtint_enable(softc);
|
|
(void)sleep((caddr_t)softc, PWAIT);
|
|
(void)splx(nx);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return(EINVAL);
|
|
}
|
|
break;
|
|
|
|
case FBVIDEOFMT:
|
|
|
|
if((*data >= VX_VIDFMT_1280_76Hz) && (*data <= VX_VIDFMT_USER)) {
|
|
softc->videofmt = *data;
|
|
softc->w = softc->gattr.fbtype.fb_width =
|
|
videofmt[softc->videofmt].x;
|
|
softc->h = softc->gattr.fbtype.fb_height =
|
|
videofmt[softc->videofmt].y;
|
|
softc->clk = videofmt[softc->videofmt].clk;
|
|
softc->hz = videofmt[softc->videofmt].hz;
|
|
softc->gattr.fbtype.fb_size = softc->w * softc->h;
|
|
|
|
} else {
|
|
printf ("ioctl: vx: FBVIDEOFMT illegal type\n");
|
|
return (EINVAL);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return (-1);
|
|
|
|
} /* switch (cmd) */
|
|
return (0);
|
|
}
|
|
|
|
#ifdef notdef
|
|
static
|
|
vxtout(arg)
|
|
caddr_t arg;
|
|
{
|
|
vx_vrtint_enable(((struct vx_softc *)arg));
|
|
}
|
|
#endif
|
|
|
|
static
|
|
synctovrt(softc)
|
|
struct vx_softc *softc;
|
|
{
|
|
int nx;
|
|
|
|
while(cgvx_intr_pending(softc)) {
|
|
#ifdef notdef
|
|
timeout(vxtout, softc, hz/30);
|
|
#endif
|
|
nx = splr(pritospl(softc->pri));
|
|
softc->intr |= VX_VRTWAIT;
|
|
vx_vrtint_enable(softc);
|
|
(void)sleep((caddr_t)softc, PWAIT);
|
|
(void)splx(nx);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
vxioctl(dev, cmd, data, flag)
|
|
dev_t dev;
|
|
caddr_t data;
|
|
int cmd, flag;
|
|
{
|
|
struct vx_softc *vx = getsoftc(minor(dev));
|
|
struct vx_nodeinfo *vn;
|
|
struct vx_procinfo *vp;
|
|
struct vx_parms *vip;
|
|
int rval;
|
|
int vxunkey();
|
|
int fc;
|
|
|
|
if ((rval = cgvxioctl(dev, cmd, data, flag)) != -1)
|
|
return(rval);
|
|
|
|
switch (cmd) {
|
|
case VXNDMAP:
|
|
case VXLOCK:
|
|
case VXTEST:
|
|
case VXALLOCKEY:
|
|
case VXFREEKEY:
|
|
case VXFREELOCK:
|
|
break;
|
|
default:
|
|
vip = (struct vx_parms *)data;
|
|
|
|
if (vip->vip_unit < 0 || vip->vip_unit > VX_MAXNODES)
|
|
return ENODEV;
|
|
vn = (struct vx_nodeinfo *)&(vx->vx_nodeinfo[vip->vip_unit]);
|
|
|
|
if ((vp = vxgetproc(dev)) == NULL)
|
|
return ENXIO;
|
|
break;
|
|
}
|
|
|
|
switch (cmd){
|
|
case VXTEST:
|
|
{
|
|
int x;
|
|
struct vx_vmectl *nvmectl = (struct vx_vmectl *)vx->va[MAP_VMECTL];
|
|
|
|
x = splr(pritospl(vx->pri));
|
|
vx->intr |= VX_VRTWAIT;
|
|
vx_pgmint_enable(vx);
|
|
nvmectl->intr = 0;
|
|
(void)sleep((caddr_t)vx, PWAIT);
|
|
(void)splx(x);
|
|
return(0);
|
|
}
|
|
|
|
case VXENINTR:
|
|
#ifdef notdef
|
|
printf("enabling interrupt for node=%x u = %x\n",vip->vip_unit,u.u_procp->p_pid);
|
|
#endif
|
|
vx_pgmint_enable(vx);
|
|
VXSET(vp->vp_intrmask, vip->vip_unit);
|
|
break;
|
|
|
|
case VXLOCK:
|
|
{
|
|
struct vx_procinfo *nvp;
|
|
int i;
|
|
|
|
vip = (struct vx_parms *)data;
|
|
|
|
if (vip->vip_unit < 0 || vip->vip_unit > VX_MAXNODES)
|
|
return(ENODEV);
|
|
|
|
if (VXISSET(vx->vx_ndmask, vip->vip_unit) == 0)
|
|
return(ENODEV);
|
|
|
|
vn = (struct vx_nodeinfo *)&(vx->vx_nodeinfo[vip->vip_unit]);
|
|
|
|
if ((nvp = vxgetproc(dev)) == NULL) {
|
|
|
|
nvp = vx->vx_procinfo;
|
|
for (i = 0; i < VX_MAXPROCS; i++, nvp++){
|
|
if (nvp->vp_procp == NULL)
|
|
break;
|
|
}
|
|
|
|
if (i >= VX_MAXPROCS)
|
|
return ENXIO;
|
|
|
|
nvp->vp_procp = u.u_procp;
|
|
nvp->vp_lockmask = 0;
|
|
vx->vx_dev = dev;
|
|
}
|
|
|
|
return(vxlock(vn, nvp, vip->vip_unit));
|
|
}
|
|
|
|
case VXUNLOCK:
|
|
return(vxunlock(vn, vp, vip->vip_unit));
|
|
|
|
case VXFREELOCK:
|
|
{
|
|
int n, p, ndmask;
|
|
|
|
ndmask = *(int *)data;
|
|
vn = vx->vx_nodeinfo;
|
|
for (n = 0; n < VX_MAXNODES; n++, vn++)
|
|
if (VXISSET(vx->vx_ndmask, n) && VXISSET(ndmask, n)) {
|
|
vp = vx->vx_procinfo;
|
|
for (p = 0; p < VX_MAXPROCS; p++, vp++)
|
|
if (VXISSET(vp->vp_lockmask, n)) {
|
|
VXCLEAR(vp->vp_lockmask, n);
|
|
vn->vn_lockflag = 0;
|
|
vn->vn_procp = NULL;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case VXEXPORTSEG:
|
|
vn->vn_mseg.vms_addr = vip->vip_addr;
|
|
vn->vn_mseg.vms_size = vip->vip_size;
|
|
vn->vn_mseg.vms_pp = u.u_procp;
|
|
|
|
vn->vn_omseg.vms_addr = NULL;
|
|
vn->vn_omseg.vms_pp = u.u_procp;
|
|
break;
|
|
|
|
case VXUNEXPORTSEG:
|
|
if (vn->vn_omseg.vms_addr != NULL){
|
|
fc = (int)as_fault(vn->vn_omseg.vms_pp->p_as,vn->vn_omseg.vms_addr,PAGESIZE,F_SOFTUNLOCK,S_WRITE);
|
|
if (fc)
|
|
panic("vxioctl: fault");
|
|
vn->vn_omseg.vms_addr = NULL;
|
|
mbrelse(vxinfo[minor(dev)]->md_hd, &vn->vn_mbinfo);
|
|
}
|
|
break;
|
|
|
|
case VXNDMAP:
|
|
{
|
|
int *tmp=(int*)data;
|
|
*tmp = vx->vx_ndmask;
|
|
}
|
|
break;
|
|
case VXALLOCKEY:
|
|
{
|
|
int key = *(int *)data;
|
|
|
|
if (key < 0 || key >= VX_NKEYS)
|
|
return(ENODEV);
|
|
|
|
if (vx->vx_keyprocs[key] == NULL) {
|
|
vx->vx_keyprocs[key] = u.u_procp;
|
|
return(0);
|
|
}
|
|
return(EBUSY);
|
|
}
|
|
case VXFREEKEY:
|
|
{
|
|
int key = *(int *)data;
|
|
|
|
return(vxunkey(vx, key));
|
|
}
|
|
|
|
default:
|
|
return(-1);
|
|
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
vxunkey(vx, key)
|
|
struct vx_softc *vx;
|
|
int key;
|
|
{
|
|
struct cgvx_vidmix *vidmix =
|
|
(struct cgvx_vidmix *)vx->va[MAP_VIDMIX];
|
|
|
|
if (key < 0 || key >= VX_NKEYS)
|
|
return(ENODEV);
|
|
|
|
if (vx->vx_keyprocs[key] != u.u_procp)
|
|
return(EPERM);
|
|
|
|
vx->vx_keyprocs[key] = NULL;
|
|
vidmix->vidmixcsr &= ~(0x01010101 << key);
|
|
return(0);
|
|
}
|
|
|
|
vxlock(vn, vp, unit)
|
|
struct vx_nodeinfo *vn;
|
|
struct vx_procinfo *vp;
|
|
{
|
|
if (vn->vn_lockflag)
|
|
return(EBUSY);
|
|
|
|
vn->vn_lockflag++;
|
|
vn->vn_procp = u.u_procp;
|
|
VXSET(vp->vp_lockmask, unit);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
vxunlock(vn, vp, unit)
|
|
struct vx_nodeinfo *vn;
|
|
struct vx_procinfo *vp;
|
|
int unit;
|
|
{
|
|
if (vn->vn_procp != u.u_procp)
|
|
return(EPERM);
|
|
if (!vn->vn_lockflag)
|
|
return(0);
|
|
|
|
VXCLEAR(vp->vp_lockmask, unit);
|
|
vn->vn_lockflag = 0;
|
|
vn->vn_procp = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*ARGSUSED*/
|
|
vxread(dev,uio)
|
|
dev_t dev; struct uio *uio;
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
|
|
/*ARGSUSED*/
|
|
vxwrite(dev,uio)
|
|
dev_t dev; struct uio *uio;
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
|
|
#if NWIN > 0
|
|
|
|
/*
|
|
* SunWindows specific stuff
|
|
*/
|
|
|
|
/*
|
|
* Mem_rop which waits for idle FBC
|
|
* If we do not wait, it seems that
|
|
* bus timeout errors occur.
|
|
* The wait is limited to 50 millisecs.
|
|
*/
|
|
|
|
static cg6_rop_wait = 50;
|
|
|
|
static
|
|
cgvx_mem_rop(dpr, dx, dy, dw, dh, op, spr, sx, sy)
|
|
Pixrect *dpr;
|
|
int dx, dy, dw, dh, op;
|
|
Pixrect *spr;
|
|
int sx, sy;
|
|
{
|
|
Pixrect mpr;
|
|
int unit;
|
|
|
|
/* Parallax tvone fix
|
|
/* if (spr && spr->pr_ops == &cg6_ops) {*/
|
|
/* FIXME: bugginess here... */
|
|
if (spr && spr->pr_ops->pro_rop == cgvx_mem_rop) {
|
|
/* end Parallax tvone fix
|
|
*/
|
|
unit = mpr_d(spr)->md_primary;
|
|
mpr = *spr;
|
|
mpr.pr_ops = &mem_ops;
|
|
spr = &mpr;
|
|
} else
|
|
unit = mpr_d(dpr)->md_primary;
|
|
|
|
if (cg6_rop_wait) {
|
|
register u_int *statp =&((struct fbc *)
|
|
vx_softc[unit].va[MAP_FBCTEC])->l_fbc_status;
|
|
CDELAY(!(*statp & L_FBC_BUSY), cg6_rop_wait << 10);
|
|
}
|
|
return (mem_rop(dpr, dx, dy, dw, dh, op, spr, sx, sy));
|
|
}
|
|
|
|
static
|
|
cgvx_putcolormap(pr, index, count, red, green, blue)
|
|
Pixrect *pr;
|
|
int index, count;
|
|
unsigned char *red, *green, *blue;
|
|
{
|
|
register struct vx_softc *softc = getsoftc((mpr_d(pr)->md_primary));
|
|
static void cgvx_update_cmap();
|
|
|
|
register u_int rindex = (u_int) index;
|
|
register u_int rcount = (u_int) count;
|
|
register u_char *map;
|
|
register u_int entries;
|
|
|
|
map = softc->cmap_rgb;
|
|
entries = CG6_CMAP_ENTRIES;
|
|
|
|
/* check arguments */
|
|
if (rindex >= entries || rindex + rcount > entries) {
|
|
return (PIX_ERR);
|
|
}
|
|
|
|
if (rcount == 0)
|
|
return (0);
|
|
|
|
/* lock out updates of the hardware colormap */
|
|
if (cgvx_intr_pending(softc)) {
|
|
if (!servicing_interrupt())
|
|
synctovrt(softc);
|
|
vx_vrtint_disable(softc);
|
|
}
|
|
|
|
CG6BCOPY(red, map, rcount);
|
|
CG6BCOPY(green, map+rcount, rcount);
|
|
CG6BCOPY(blue, map+2*rcount, rcount);
|
|
|
|
cgvx_update_cmap(softc, rindex, rcount);
|
|
|
|
/* enable interrupt so we can load the hardware colormap */
|
|
vx_vrtint_enable(softc);
|
|
|
|
return (0);
|
|
}
|
|
|
|
#endif NWIN > 0
|
|
|
|
/*
|
|
* enable/disable/update HW cursor
|
|
*/
|
|
static
|
|
cgvx_setcurpos(softc)
|
|
struct vx_softc *softc;
|
|
{
|
|
if (softc->cur.video_blanking == 0) {
|
|
softc->thc->l_thc_cursor =
|
|
softc->cur.enable ?
|
|
((softc->cur.pos.x - softc->cur.hot.x) << 16) |
|
|
((softc->cur.pos.y - softc->cur.hot.y) & 0xffff) :
|
|
CGVX_CURSOR_OFFPOS;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* load HW cursor bitmaps
|
|
*/
|
|
static
|
|
cgvx_setcurshape(softc)
|
|
struct vx_softc *softc;
|
|
{
|
|
u_long tmp, edge = 0;
|
|
u_long *image, *mask, *hw;
|
|
int i;
|
|
|
|
/* compute right edge mask */
|
|
if (softc->cur.size.x)
|
|
edge = (u_long) ~0 << (32 - softc->cur.size.x);
|
|
|
|
image = softc->cur.image;
|
|
mask = softc->cur.mask;
|
|
hw = (u_long *) &softc->thc->l_thc_cursora00;
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
hw[i] = (tmp = mask[i] & edge);
|
|
hw[i + 32] = tmp & image[i];
|
|
}
|
|
}
|
|
|
|
cgvxintr(softc)
|
|
register struct vx_softc *softc;
|
|
{
|
|
u_int *cmap = (u_int *)softc->va[MAP_CMAP];
|
|
u_int *omap = ((u_int *)softc->va[MAP_CMAP]) + VX_BTOVERLAY;
|
|
struct fbc *fbc = (struct fbc *)softc->va[MAP_FBCTEC];
|
|
char *somap = (char *)softc->omap_image.omap_char;
|
|
|
|
#ifdef notdef
|
|
untimeout(vxtout, softc);
|
|
#endif
|
|
|
|
/* load cursor color map */
|
|
if (softc->omap_update) {
|
|
VXDELAY ((fbc->l_fbc_status & L_FBC_BUSY), 100);
|
|
*omap++ = 0;
|
|
*omap++ = (*somap | (*(somap+2)<<8) | (*(somap+4)<<16));
|
|
*omap++ = 0;
|
|
*omap++ = (*(somap+1) | (*(somap+3)<<8) | (*(somap+5)<<16));
|
|
softc->omap_update = 0;
|
|
}
|
|
|
|
/* load main color map */
|
|
if (softc->cmap_count) {
|
|
VXDELAY ((fbc->l_fbc_status & L_FBC_BUSY), 100);
|
|
cgvxCMap_put(0, softc->cmap_index, softc->cmap_count,
|
|
softc->cmap_rgb, cmap);
|
|
softc->cmap_count = 0;
|
|
}
|
|
|
|
if (softc->intr & VX_VRTWAIT) {
|
|
softc->intr &= ~VX_VRTWAIT;
|
|
wakeup((caddr_t)softc);
|
|
}
|
|
|
|
vx_vrtint_disable(softc);
|
|
}
|
|
|
|
/*
|
|
* Initialize a colormap: background = white, all others = black
|
|
*/
|
|
static void
|
|
cgvx_reset_cmap(cmap, entries)
|
|
register caddr_t cmap;
|
|
register u_int entries;
|
|
{
|
|
bzero(cmap, entries * 3);
|
|
*cmap++ = 255;
|
|
*cmap++ = 255;
|
|
*cmap = 255;
|
|
}
|
|
|
|
/*
|
|
* Compute color map update parameters: starting index and count.
|
|
* If count is already nonzero, adjust values as necessary.
|
|
* Zero count argument indicates cursor color map update desired.
|
|
*/
|
|
static void
|
|
cgvx_update_cmap(softc, index, count)
|
|
struct vx_softc *softc;
|
|
u_int index, count;
|
|
{
|
|
u_int high, low;
|
|
|
|
if (count == 0) {
|
|
softc->omap_update = 1;
|
|
return;
|
|
}
|
|
|
|
if (high = softc->cmap_count) {
|
|
high += (low = softc->cmap_index);
|
|
|
|
if (index < low)
|
|
softc->cmap_index = low = index;
|
|
|
|
if (index + count > high)
|
|
high = index + count;
|
|
|
|
softc->cmap_count = high - low;
|
|
}
|
|
else {
|
|
softc->cmap_index = index;
|
|
softc->cmap_count = count;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Copy colormap entries between red, green, or blue array and
|
|
* interspersed rgb array.
|
|
*
|
|
* count > 0 : copy count bytes from buf to rgb
|
|
* count < 0 : copy -count bytes from rgb to buf
|
|
*/
|
|
static void
|
|
cgvx_cmap_bcopy(bufp, rgb, count)
|
|
register u_char *bufp, *rgb;
|
|
u_int count;
|
|
{
|
|
register int rcount = count;
|
|
|
|
if (--rcount >= 0)
|
|
do {
|
|
*rgb = *bufp++;
|
|
rgb += 3;
|
|
} while (--rcount >= 0);
|
|
else {
|
|
rcount = -rcount - 2;
|
|
do {
|
|
*bufp++ = *rgb;
|
|
rgb += 3;
|
|
} while (--rcount >= 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Save fbc pointed to by fbc into fbc0
|
|
* Have to do individual fields because
|
|
* some registers can not be read (eg. read activated).
|
|
*/
|
|
static
|
|
void
|
|
savefbc(fbc0, fbc, dir)
|
|
struct fbc *fbc0, *fbc;
|
|
{
|
|
if (fbc == 0 || fbc0 == 0) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Wait until the cg6 is idle.
|
|
* If not, may get a bus timeout error
|
|
*/
|
|
if (dir == DIR_FROM_FBC) {
|
|
VXDELAY ( (fbc->l_fbc_status & L_FBC_BUSY), 200);
|
|
} else {
|
|
VXDELAY ( (fbc0->l_fbc_status & L_FBC_BUSY), 200);
|
|
}
|
|
|
|
ShowIt (fbc0->l_fbc_misc, fbc->l_fbc_misc);
|
|
ShowIt (fbc0->l_fbc_rasteroffx, fbc->l_fbc_rasteroffx);
|
|
ShowIt (fbc0->l_fbc_rasteroffy, fbc->l_fbc_rasteroffy);
|
|
ShowIt (fbc0->l_fbc_rasterop, fbc->l_fbc_rasterop);
|
|
ShowIt (fbc0->l_fbc_pixelmask, fbc->l_fbc_pixelmask);
|
|
ShowIt (fbc0->l_fbc_planemask, fbc->l_fbc_planemask);
|
|
ShowIt (fbc0->l_fbc_fcolor, fbc->l_fbc_fcolor);
|
|
ShowIt (fbc0->l_fbc_bcolor, fbc->l_fbc_bcolor);
|
|
ShowIt (fbc0->l_fbc_clipminx, fbc->l_fbc_clipminx);
|
|
ShowIt (fbc0->l_fbc_clipminy, fbc->l_fbc_clipminy);
|
|
ShowIt (fbc0->l_fbc_clipmaxx, fbc->l_fbc_clipmaxx);
|
|
ShowIt (fbc0->l_fbc_clipmaxy, fbc->l_fbc_clipmaxy);
|
|
ShowIt (fbc0->l_fbc_autoincx, fbc->l_fbc_autoincx);
|
|
ShowIt (fbc0->l_fbc_autoincy, fbc->l_fbc_autoincy);
|
|
ShowIt (fbc0->l_fbc_clipcheck, fbc->l_fbc_clipcheck);
|
|
}
|
|
|
|
/*
|
|
* Enable cg6 video if on non-zero,
|
|
* else disable.
|
|
*/
|
|
cgvx_videnable(softc, on)
|
|
struct vx_softc *softc;
|
|
u_int on;
|
|
{
|
|
register struct thc *thc;
|
|
|
|
thc = (struct thc*) (softc->va[MAP_FHCTHC] + CG6_THC_POFF);
|
|
|
|
if (on)
|
|
thc->l_thc_hcmisc = 0x0004cf;
|
|
else
|
|
thc->l_thc_hcmisc = 0x0000cf;
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static
|
|
void
|
|
cgvxCMap_put (cmd, index, count, cmap, colormap)
|
|
int cmd;
|
|
unsigned int count, index, *colormap;
|
|
u_char *cmap;
|
|
{
|
|
register int i, ii;
|
|
|
|
if(count+index <= 256) {
|
|
for (i=0, ii=index; i<count; i++, ii++) {
|
|
colormap[ii] = (u_int)
|
|
( *(cmap+i) | (*(cmap+count+i)<<8) | (*(cmap+2*count+i)<<16) );
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
void
|
|
cgvxCMap_get (cmd, index, count, cmap, colormap)
|
|
int cmd;
|
|
unsigned int count, index, *colormap;
|
|
u_char *cmap;
|
|
{
|
|
register int i, ii;
|
|
|
|
if(count+index <= 256) {
|
|
for (i=0, ii=index; i<count; i++, ii++) {
|
|
switch (cmd) {
|
|
case 0:
|
|
*(cmap+i) = (u_char)( colormap[ii] & 0x0000ff );
|
|
break;
|
|
case 1:
|
|
*(cmap+i) = (u_char)( (colormap[ii] & 0x00ff00) >> 8);
|
|
break;
|
|
case 2:
|
|
*(cmap+i) = (u_char)( (colormap[ii] & 0xff0000) >> 16);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef notdef
|
|
static
|
|
void
|
|
cgvxColors_put (index, count, red, green, blue, colormap)
|
|
unsigned int count, index, *colormap;
|
|
u_char *red, *green, *blue;
|
|
{
|
|
register int i, ii, error;
|
|
|
|
if(count+index <= 256) {
|
|
for (i=0, ii=index; i<count; i++, ii++) {
|
|
colormap[ii] = (unsigned int)
|
|
( *(red+i) | (*(green+i)<<8) | (*(blue+i)<<16) );
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
void
|
|
cgvxColors_get (index, count, red, green, blue, colormap)
|
|
unsigned int count, index, *colormap;
|
|
u_char *red, *green, *blue;
|
|
{
|
|
register int i, ii, error;
|
|
|
|
if(count+index <= 256) {
|
|
for (i=0, ii=index; i<count; i++, ii++) {
|
|
*(red+i) = (u_char)( (colormap[ii] & 0x0000ff) );
|
|
*(green+i) = (u_char)( (colormap[ii] & 0x00ff00) >> 8 );
|
|
*(blue+i) = (u_char)( (colormap[ii] & 0xff0000) >> 16 );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Zero all registers first!!!!! */
|
|
static
|
|
void
|
|
cgvxPAC1000 (softc, video, pac)
|
|
struct vx_softc *softc;
|
|
unsigned int *video, *pac;
|
|
{
|
|
|
|
struct cgvx_vidctl4 *t =(struct cgvx_vidctl4 *)pac;
|
|
*(video) &= 0xc0; /* stop the PAC video controller */
|
|
*(video) |= (softc->clk<<1); /* Change oscillator */
|
|
*(video) |= 0x01; /* restart the PAC video controller */
|
|
ShowIt(t->vc_h_syncsize , cgvx_vidfmt4[softc->videofmt].vc_h_syncsize);
|
|
ShowIt(t->vc_h_bporch , cgvx_vidfmt4[softc->videofmt].vc_h_bporch);
|
|
ShowIt(t->vc_h_active , cgvx_vidfmt4[softc->videofmt].vc_h_active);
|
|
ShowIt(t->vc_h_fporch , cgvx_vidfmt4[softc->videofmt].vc_h_fporch);
|
|
ShowIt(t->vc_csynclo , cgvx_vidfmt4[softc->videofmt].vc_csynclo);
|
|
ShowIt(t->vc_v_fporch , cgvx_vidfmt4[softc->videofmt].vc_v_fporch);
|
|
ShowIt(t->vc_v_sync , cgvx_vidfmt4[softc->videofmt].vc_v_sync);
|
|
ShowIt(t->vc_v_bporch , cgvx_vidfmt4[softc->videofmt].vc_v_bporch);
|
|
ShowIt(t->vc_v_active , cgvx_vidfmt4[softc->videofmt].vc_v_active);
|
|
ShowIt(t->vc_displaymode , cgvx_vidfmt4[softc->videofmt].vc_displaymode);
|
|
ShowIt(t->vc_casoffset , cgvx_vidfmt4[softc->videofmt].vc_casoffset);
|
|
ShowIt(t->vc_ras_tof , cgvx_vidfmt4[softc->videofmt].vc_ras_tof);
|
|
ShowIt(t->vc_cas_tof , cgvx_vidfmt4[softc->videofmt].vc_cas_tof);
|
|
ShowIt(t->vc_go , 0xffff);
|
|
}
|
|
|
|
static
|
|
void
|
|
cgvxASIC(asic)
|
|
unsigned int *asic;
|
|
{
|
|
ShowIt (*(asic + 0x09) , 0x0);
|
|
ShowIt (*(asic + 0x00) , 0x0);
|
|
ShowIt (*(asic + 0x01) , 0x0);
|
|
ShowIt (*(asic + 0x02) , 0x0);
|
|
ShowIt (*(asic + 0x03) , 0x0);
|
|
ShowIt (*(asic + 0x04) , 0x0);
|
|
ShowIt (*(asic + 0x05) , 0x0);
|
|
ShowIt (*(asic + 0x06) , 0x0);
|
|
ShowIt (*(asic + 0x07) , 0x0);
|
|
ShowIt (*(asic + 0x08) , 0x0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Initialize 8-bit colormap 0=white, 1-255=black
|
|
* Initialize 32-bit colormap to ramps
|
|
*/
|
|
static
|
|
void
|
|
cgvxRAMDAC (cmap)
|
|
unsigned int *cmap;
|
|
{
|
|
int i;
|
|
|
|
*(cmap + VX_BTCR0 ) = 0x505050;
|
|
*(cmap + VX_BTCR1 ) = 0x000000;
|
|
*(cmap + VX_BTCR2 ) = 0x080808;
|
|
*(cmap + VX_BTRDMSKLO ) = 0xffffff;
|
|
*(cmap + VX_BTRDMSKHI ) = 0x030303;
|
|
*(cmap + VX_BTBKMSKLO ) = 0x000000;
|
|
*(cmap + VX_BTBKMSKHI ) = 0x000000;
|
|
*(cmap + VX_BTOVRDMASK) = 0x000000;
|
|
*(cmap + VX_BTOVBKMASK) = 0x000000;
|
|
|
|
*(cmap) = ColorFull(0xff);
|
|
for (i=1; i<0x120; i++) *(cmap+i) = 0;
|
|
for (i=0x400; i<0x500; i++) *(cmap+i) = ColorFull(i & 0xff);
|
|
for (i=0x500; i<0x600; i++) *(cmap+i) = ColorRed (i & 0xff);
|
|
for (i=0x600; i<0x700; i++) *(cmap+i) = ColorGren(i & 0xff);
|
|
for (i=0x700; i<0x800; i++) *(cmap+i) = ColorBlue(i & 0xff);
|
|
}
|
|
|
|
|
|
static
|
|
void
|
|
cgvxFHCTHC (softc, cfg, thc)
|
|
struct vx_softc *softc;
|
|
unsigned int *cfg;
|
|
struct thc *thc;
|
|
{
|
|
int i=softc->videofmt;
|
|
*cfg = FBCRESET | MODE68 | cgvx_vid_data[i].HORZ;
|
|
*cfg = MODE68 | cgvx_vid_data[i].HORZ;
|
|
thc->l_thc_hchs = cgvx_vid_data[i].hchs;
|
|
thc->l_thc_hchsdvs = cgvx_vid_data[i].hchsdvs;
|
|
thc->l_thc_hchd = cgvx_vid_data[i].hchd;
|
|
thc->l_thc_hcvs =
|
|
Upper(cgvx_vid_data[i].VFP+1) |
|
|
Lower(cgvx_vid_data[i].VSYN +
|
|
cgvx_vid_data[i].VFP);
|
|
thc->l_thc_hcvd =
|
|
Upper(cgvx_vid_data[i].VBP + cgvx_vid_data[i].VSYN + cgvx_vid_data[i].VFP)
|
|
| Lower(cgvx_vid_data[i].VVIS + cgvx_vid_data[i].VBP +
|
|
cgvx_vid_data[i].VSYN + cgvx_vid_data[i].VFP);
|
|
thc->l_thc_hcr = 363;
|
|
i = thc->l_thc_hcmisc;
|
|
thc->l_thc_hcmisc = (i | THC_HCMISC_VIDEO |
|
|
THC_HCMISC_SYNCEN | THC_HCMISC_CURSOR_RES);
|
|
}
|
|
|
|
/*
|
|
* from here on down, is the cgvx segment driver. this virtualizes the
|
|
* lego register file by associating a register save area with each
|
|
* mapping of the cgvx device (each cgvx segment). only one of these
|
|
* mappings is valid at any time; a page fault on one of the invalid
|
|
* mappings saves off the current cgvx context, invalidates the current
|
|
* valid mapping, restores the former register contents appropriate to
|
|
* the faulting mapping, and then validates it.
|
|
*
|
|
* this implements a graphical context switch that is transparent to the user.
|
|
*
|
|
* the TEC and FBC contain the interesting context registers.
|
|
*/
|
|
|
|
/*
|
|
* many registers in the tec and fbc do
|
|
* not need to be saved/restored.
|
|
*/
|
|
struct cgvx_cntxt {
|
|
struct {
|
|
u_int mv;
|
|
u_int clip;
|
|
u_int vdc;
|
|
u_int data[64][2];
|
|
} tec;
|
|
struct {
|
|
u_int status;
|
|
u_int clipcheck;
|
|
struct l_fbc_misc misc;
|
|
u_int x0, y0, x1, y1, x2, y2, x3, y3;
|
|
u_int rasteroffx, rasteroffy;
|
|
u_int autoincx, autoincy;
|
|
u_int clipminx, clipminy, clipmaxx, clipmaxy;
|
|
u_int fcolor, bcolor;
|
|
struct l_fbc_rasterop rasterop;
|
|
u_int planemask, pixelmask;
|
|
union l_fbc_pattalign pattalign;
|
|
u_int pattern0, pattern1, pattern2, pattern3,
|
|
pattern4, pattern5, pattern6, pattern7;
|
|
} fbc;
|
|
};
|
|
|
|
struct segvx_data {
|
|
int flag;
|
|
struct cgvx_cntxt *cntxtp; /* pointer to context */
|
|
struct segvx_data *link; /* shared_list */
|
|
dev_t dev;
|
|
u_int offset;
|
|
};
|
|
|
|
struct segvx_data *vxshared_list;
|
|
struct cgvx_cntxt vxshared_context;
|
|
|
|
#define segvx_crargs segvx_data
|
|
|
|
#define SEG_CG6PRIV (1) /* cg6 private context */
|
|
#define SEG_CG6SHARED (2) /* cg6 shared context */
|
|
#define SEG_LOCK (3) /* lock page */
|
|
#define SEG_VX (4) /* VX segment (whole vx) */
|
|
#define SEG_MISC (5) /* other cg6/VX bits */
|
|
|
|
|
|
/*
|
|
* Window Grabber lock management
|
|
*/
|
|
|
|
#define NLOCKS 16
|
|
#define LOCKTIME 5 /* seconds */
|
|
static int locktime = LOCKTIME;
|
|
|
|
/* flag bits */
|
|
#define LOCKMAP 0x01
|
|
#define UNLOCKMAP 0x02
|
|
#define ATTACH 0x04
|
|
#define TRASHPAGE 0x08
|
|
#define UNGRAB 0x10 /* ungrab called */
|
|
|
|
struct segproc {
|
|
int flag;
|
|
struct proc *procp;
|
|
caddr_t lockaddr;
|
|
struct seg *locksegp;
|
|
caddr_t unlockaddr;
|
|
struct seg *unlocksegp;
|
|
};
|
|
|
|
struct seglock {
|
|
short sleepf; /* sleep flag */
|
|
short allocf; /* allocated flag */
|
|
off_t offset; /* offset into device */
|
|
int *page; /* virtual address of lock page */
|
|
struct segproc cr; /* creator */
|
|
struct segproc cl; /* client */
|
|
struct segproc *owner; /* current owner of lock */
|
|
struct segproc *other; /* not owner of lock */
|
|
};
|
|
|
|
caddr_t vxtrashpage; /* for trashing unwanted writes */
|
|
|
|
struct seglock vxlock_list[NLOCKS];
|
|
struct seglock *segvx_findlock();
|
|
|
|
/*
|
|
* This routine is called through the cdevsw[] table to handle
|
|
* the creation of cgvx and vx segments.
|
|
*/
|
|
/*ARGSUSED*/
|
|
int
|
|
vxsegmap(dev, offset, as, addr, len, prot, maxprot, flags, cred)
|
|
dev_t dev;
|
|
register u_int offset;
|
|
struct as *as;
|
|
addr_t *addr;
|
|
register u_int len;
|
|
u_int prot, maxprot;
|
|
u_int flags;
|
|
struct ucred *cred;
|
|
{
|
|
struct segvx_crargs dev_a;
|
|
int segvx_create();
|
|
register i;
|
|
caddr_t seglock;
|
|
int ctxflag = 0;
|
|
int vxflag = 0;
|
|
|
|
register int unit = minor(dev);
|
|
struct vx_softc *softc = getsoftc(unit);
|
|
|
|
enum as_res as_unmap();
|
|
int as_map();
|
|
|
|
#ifndef _sys_mman_h
|
|
int old_mmap;
|
|
#endif
|
|
|
|
DEBUGprint (0x1, "vxsegmap \n", 0, 0);
|
|
|
|
/*
|
|
* we now support MAP_SHARED and MAP_PRIVATE:
|
|
*
|
|
* MAP_SHARED means you get the shared context
|
|
* which is the traditional mapping method.
|
|
*
|
|
* MAP_PRIVATE means you get your very own
|
|
* LEGO context.
|
|
*
|
|
* Note that you can't get to here without
|
|
* asking for one or the other, but not both.
|
|
*/
|
|
|
|
#ifndef _sys_mman_h
|
|
/*
|
|
* See if this is an "old mmap call". If so, remember this
|
|
* fact and convert the flags value given to mmap to indicate
|
|
* the specified address in the system call must be used.
|
|
* _MAP_NEW is turned set by all new uses of mmap.
|
|
*/
|
|
old_mmap = (flags & _MAP_NEW) == 0;
|
|
if (old_mmap)
|
|
flags |= MAP_FIXED;
|
|
flags &= ~_MAP_NEW;
|
|
#endif
|
|
|
|
/*
|
|
* Check to see if this is an allocated lock page.
|
|
*/
|
|
|
|
if ((seglock =
|
|
(caddr_t)segvx_findlock((off_t)offset, softc)) !=
|
|
(caddr_t)NULL) {
|
|
DEBUGprint (0x1, "cgsixsegmap findlock=%x len=%d\n", seglock, len);
|
|
if (len != PAGESIZE)
|
|
return (EINVAL);
|
|
} else {
|
|
/*
|
|
* Check to insure that the entire range is
|
|
* legal and we are not trying to map in
|
|
* more than the device will let us.
|
|
*/
|
|
for (i = 0; i < len; i += PAGESIZE) {
|
|
if (cgvxmmap(dev, (off_t)offset + i, (int)maxprot) == -1) {
|
|
return (ENXIO);
|
|
}
|
|
if ((offset+i >= (off_t)softc->addrs[MAP_FBCTEC].vaddr) &&
|
|
(offset+i < (off_t)softc->addrs[MAP_FBCTEC].vaddr+
|
|
softc->addrs[MAP_FBCTEC].size))
|
|
ctxflag++;
|
|
}
|
|
|
|
/* check to see if this is the VX portion of the device */
|
|
|
|
if (offset >= (off_t)softc->addrs[MAP_VX].vaddr &&
|
|
offset <= (off_t)softc->addrs[MAP_VX].vaddr +
|
|
softc->addrs[MAP_VX].size)
|
|
vxflag++;
|
|
}
|
|
|
|
if ((flags & MAP_FIXED) == 0) {
|
|
/*
|
|
* Pick an address w/o worrying about
|
|
* any vac alignment contraints.
|
|
*/
|
|
map_addr(addr, len, (off_t)0, 0);
|
|
DEBUGprint (0x1, "cgsixsegmap map_addr=%x len=%d\n", *addr, len);
|
|
if (*addr == NULL)
|
|
return (ENOMEM);
|
|
} else {
|
|
/*
|
|
* User specified address -
|
|
* Blow away any previous mappings.
|
|
*/
|
|
DEBUGprint (0x1, "cgsixsegmap as_unmap MAP_FIXED=%x len=%d\n", *addr, len);
|
|
i = (int)as_unmap((struct as *)as, *addr, len);
|
|
}
|
|
|
|
/*
|
|
* record device number; mapping function doesn't do anything smart
|
|
* with it, but it takes it as an argument. the offset is needed
|
|
* for mapping objects beginning some ways into them.
|
|
*/
|
|
|
|
dev_a.dev = dev;
|
|
dev_a.offset = offset;
|
|
dev_a.cntxtp = NULL;
|
|
dev_a.link = NULL;
|
|
|
|
/*
|
|
* determine whether this is a shared mapping, or a private
|
|
* one. if shared, link it onto the shared list, if private,
|
|
* create a private LEGO context.
|
|
*/
|
|
if (seglock) {
|
|
dev_a.flag = SEG_LOCK;
|
|
} else
|
|
if (vxflag) {
|
|
dev_a.flag = SEG_VX;
|
|
} else
|
|
if (ctxflag) {
|
|
if (flags & MAP_SHARED) { /* shared mapping */
|
|
struct segvx_crargs *a;
|
|
a = vxshared_list;
|
|
vxshared_list = &dev_a;
|
|
dev_a.flag = SEG_CG6SHARED;
|
|
dev_a.link = a;
|
|
dev_a.cntxtp = &vxshared_context;
|
|
} else { /* private mapping */
|
|
dev_a.flag = SEG_CG6PRIV;
|
|
dev_a.cntxtp =
|
|
(struct cgvx_cntxt *)new_kmem_zalloc(
|
|
sizeof (struct cgvx_cntxt),
|
|
KMEM_SLEEP);
|
|
}
|
|
} else
|
|
dev_a.flag = SEG_MISC;
|
|
return (as_map((struct as *)as, *addr, len, segvx_create, (caddr_t)&dev_a));
|
|
}
|
|
|
|
static int segvx_dup(/* seg, newsegp */);
|
|
static int segvx_unmap(/* seg, addr, len */);
|
|
static int segvx_free(/* seg */);
|
|
static faultcode_t segvx_fault(/* seg, addr, len, type, rw */);
|
|
static int segvx_checkprot(/* seg, addr, size, prot */);
|
|
static int segvx_badop();
|
|
static int segvx_incore();
|
|
|
|
struct seg_ops segvx_ops = {
|
|
segvx_dup, /* dup */
|
|
segvx_unmap, /* unmap */
|
|
segvx_free, /* free */
|
|
segvx_fault, /* fault */
|
|
segvx_badop, /* faulta */
|
|
segvx_badop, /* unload */
|
|
segvx_badop, /* setprot */
|
|
segvx_checkprot, /* checkprot */
|
|
segvx_badop, /* kluster */
|
|
(u_int (*)()) NULL, /* swapout */
|
|
segvx_badop, /* sync */
|
|
segvx_incore /* incore */
|
|
};
|
|
|
|
/*
|
|
* Window Grabber Lock Page Management
|
|
*/
|
|
|
|
/*
|
|
* search the vxlock_list list for the specified offset
|
|
*
|
|
*/
|
|
|
|
/*ARGSUSED*/
|
|
struct seglock *
|
|
segvx_findlock(off, softc)
|
|
off_t off;
|
|
struct vx_softc *softc;
|
|
{
|
|
register int i;
|
|
|
|
DEBUGprint (0x1, "segvx_findlock \n", 0, 0);
|
|
|
|
if ((unsigned long)off < (unsigned long)softc->lockbase)
|
|
return ((struct seglock *)NULL);
|
|
for (i = 0; i < NLOCKS; i++)
|
|
if (off == vxlock_list[i].offset)
|
|
return (vxlock_list + i);
|
|
return ((struct seglock *)NULL);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
segvx_grabattach(cookiep) /* IOCTL */
|
|
caddr_t cookiep;
|
|
{
|
|
DEBUGprint (0x1, "segvx_grabattach \n", 0, 0);
|
|
|
|
return (EINVAL);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
segvx_grabinfo(pp) /* IOCTL */
|
|
caddr_t pp;
|
|
{
|
|
DEBUGprint (0x1, "segvx_grabinfo \n", 0, 0);
|
|
|
|
*(int *)pp = 0;
|
|
return (0);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
segvx_graballoc(pp, softc) /* IOCTL */
|
|
caddr_t pp;
|
|
struct vx_softc *softc;
|
|
{
|
|
register int i;
|
|
register struct seglock *lp = vxlock_list;
|
|
|
|
DEBUGprint (0x1, "segvx_graballoc \n", 0, 0);
|
|
|
|
if (vxtrashpage == NULL) {
|
|
vxtrashpage = getpages(1, KMEM_SLEEP);
|
|
for (i = 0; i < NLOCKS; i++)
|
|
vxlock_list[i].offset = (off_t)(softc->lockbase+PAGESIZE*i);
|
|
}
|
|
|
|
for (i = 0; i < NLOCKS; i++, lp++)
|
|
if (lp->allocf == 0) {
|
|
lp->allocf = 1;
|
|
if (lp->page == (int *)NULL)
|
|
lp->page = (int *)getpages(1, KMEM_SLEEP);
|
|
lp->cr.flag = 0;
|
|
lp->cl.flag = 0;
|
|
lp->cr.procp = u.u_procp;
|
|
lp->cl.procp = NULL;
|
|
lp->cr.locksegp = NULL;
|
|
lp->cl.locksegp = NULL;
|
|
lp->cr.unlocksegp = NULL;
|
|
lp->cl.unlocksegp = NULL;
|
|
lp->owner = NULL;
|
|
lp->other = NULL;
|
|
lp->sleepf = 0;
|
|
*lp->page = 0;
|
|
*(int *)pp = (int)lp->offset;
|
|
return (0);
|
|
}
|
|
return (ENOMEM);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
segvx_grabfree(pp, softc) /* IOCTL */
|
|
caddr_t pp;
|
|
struct vx_softc *softc;
|
|
{
|
|
register struct seglock *lp;
|
|
static int segvx_timeout();
|
|
|
|
DEBUGprint (0x1, "segvx_grabfree \n", 0, 0);
|
|
|
|
if ((lp = segvx_findlock((off_t)*(int *)pp, softc)) == NULL)
|
|
return(EINVAL);
|
|
lp->cr.flag |= UNGRAB;
|
|
lp->allocf = 0;
|
|
if (lp->sleepf) { /* client will acquire the lock in this case */
|
|
untimeout(segvx_timeout, (caddr_t)lp);
|
|
wakeup((caddr_t)lp->page);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Create a device segment. the lego context is initialized by the create args.
|
|
*/
|
|
static
|
|
segvx_create(seg, argsp)
|
|
struct seg *seg;
|
|
caddr_t argsp;
|
|
{
|
|
register struct segvx_data *dp;
|
|
|
|
DEBUGprint (0x1, "segvx_create \n", 0, 0);
|
|
|
|
dp = (struct segvx_data *)
|
|
new_kmem_zalloc(sizeof (struct segvx_data), KMEM_SLEEP);
|
|
*dp = *((struct segvx_crargs *)argsp);
|
|
|
|
seg->s_ops = &segvx_ops;
|
|
seg->s_data = (char *)dp;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Duplicate seg and return new segment in newsegp. copy original lego context.
|
|
*/
|
|
static
|
|
segvx_dup(seg, newseg)
|
|
struct seg *seg, *newseg;
|
|
{
|
|
register struct segvx_data *sdp = (struct segvx_data *)seg->s_data;
|
|
register struct segvx_data *ndp;
|
|
|
|
DEBUGprint (0x1, "segvx_dup \n", 0, 0);
|
|
|
|
(void) segvx_create(newseg, (caddr_t)sdp);
|
|
ndp = (struct segvx_data *)newseg->s_data;
|
|
|
|
switch(sdp->flag) {
|
|
case SEG_CG6PRIV:
|
|
ndp->cntxtp = (struct cgvx_cntxt *)
|
|
new_kmem_zalloc(sizeof (struct cgvx_cntxt), KMEM_SLEEP);
|
|
*ndp->cntxtp = *sdp->cntxtp;
|
|
break;
|
|
case SEG_CG6SHARED:
|
|
{
|
|
struct segvx_crargs *a;
|
|
|
|
a = vxshared_list;
|
|
vxshared_list = ndp;
|
|
ndp->link = a;
|
|
ndp->cntxtp = &vxshared_context;
|
|
break;
|
|
}
|
|
case SEG_LOCK:
|
|
case SEG_VX:
|
|
case SEG_MISC:
|
|
break;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static
|
|
segvx_unmap(seg, addr, len)
|
|
register struct seg *seg;
|
|
register addr_t addr;
|
|
u_int len;
|
|
{
|
|
register struct segvx_data *sdp = (struct segvx_data *)seg->s_data;
|
|
struct vx_softc *vx = getsoftc(minor(sdp->dev));
|
|
register struct seg *nseg;
|
|
addr_t nbase;
|
|
u_int nsize;
|
|
void hat_unload();
|
|
void hat_newseg();
|
|
static segvx_lockfree();
|
|
|
|
DEBUGprint (0x1, "segvx_unmap \n", 0, 0);
|
|
|
|
segvx_lockfree(seg, vx);
|
|
|
|
/*
|
|
* Check for bad sizes
|
|
*/
|
|
if (addr < seg->s_base || addr + len > seg->s_base + seg->s_size ||
|
|
(len & PAGEOFFSET) || ((u_int)addr & PAGEOFFSET))
|
|
panic("segdev_unmap");
|
|
|
|
/*
|
|
* Unload any hardware translations in the range to be taken out.
|
|
*/
|
|
|
|
hat_unload(seg,addr,len);
|
|
|
|
/*
|
|
* Check for entire segment
|
|
*/
|
|
|
|
if (addr == seg->s_base && len == seg->s_size) {
|
|
seg_free(seg);
|
|
return (0);
|
|
}
|
|
/*
|
|
* Check for beginning of segment
|
|
*/
|
|
|
|
if (addr == seg->s_base) {
|
|
seg->s_base += len;
|
|
seg->s_size -= len;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Check for end of segment
|
|
*/
|
|
if (addr + len == seg->s_base + seg->s_size) {
|
|
seg->s_base += len;
|
|
seg->s_size -= len;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* The section to go is in the middle of the segment,
|
|
* have to make it into two segments. nseg is made for
|
|
* the high end while seg is cut down at the low end.
|
|
*/
|
|
nbase = addr + len; /* new seg base */
|
|
nsize = (seg->s_base + seg->s_size) - nbase; /* new seg size */
|
|
seg->s_size = addr - seg->s_base; /* shrink old seg */
|
|
nseg = seg_alloc(seg->s_as, nbase, nsize);
|
|
if (nseg == NULL)
|
|
panic("segvx_unmap seg_alloc");
|
|
|
|
nseg->s_ops = seg->s_ops;
|
|
|
|
/* figure out what to do about the new context */
|
|
|
|
nseg->s_data = (caddr_t)
|
|
new_kmem_alloc(sizeof (struct segvx_data),KMEM_SLEEP);
|
|
|
|
switch(sdp->flag) {
|
|
case SEG_CG6PRIV:
|
|
((struct segvx_data *)nseg->s_data)->cntxtp =
|
|
(struct cgvx_cntxt *)
|
|
new_kmem_zalloc(sizeof (struct cgvx_cntxt),KMEM_SLEEP);
|
|
*((struct segvx_data *)nseg->s_data)->cntxtp = *sdp->cntxtp;
|
|
|
|
case SEG_CG6SHARED:
|
|
{
|
|
struct segvx_crargs *a;
|
|
|
|
a = vxshared_list;
|
|
vxshared_list = (struct segvx_data *)nseg->s_data;
|
|
((struct segvx_data *)nseg->s_data)->link = a;
|
|
}
|
|
case SEG_LOCK:
|
|
case SEG_MISC:
|
|
case SEG_VX:
|
|
((struct segvx_data *)nseg->s_data)->cntxtp = NULL;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Now we do something so that all the translations which used
|
|
* to be associated with seg but are now associated with nseg.
|
|
*/
|
|
hat_newseg(seg, nseg->s_base, nseg->s_size, nseg);
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* free a lego segment and clean up its context.
|
|
*/
|
|
static
|
|
int
|
|
segvx_free(seg)
|
|
struct seg *seg;
|
|
{
|
|
register struct segvx_data *sdp = (struct segvx_data *)seg->s_data;
|
|
register struct vx_softc *softc = getsoftc(minor(sdp->dev));
|
|
|
|
DEBUGprint (0x1, "segvx_free \n", 0, 0);
|
|
|
|
if (seg == softc->curcntxt)
|
|
softc->curcntxt = NULL;
|
|
|
|
switch(sdp->flag) {
|
|
case SEG_CG6PRIV:
|
|
kmem_free((char *)sdp->cntxtp, sizeof (struct cgvx_cntxt));
|
|
break;
|
|
case SEG_LOCK:
|
|
segvx_lockfree(seg, softc);
|
|
break;
|
|
case SEG_VX:
|
|
(void)vxfreethings(sdp->dev);
|
|
break;
|
|
case SEG_MISC:
|
|
case SEG_CG6SHARED:
|
|
break;
|
|
}
|
|
kmem_free((char *)sdp, sizeof (*sdp));
|
|
return(0);
|
|
}
|
|
|
|
|
|
#define CG6MAP(sp, addr, page) \
|
|
hat_devload(sp, \
|
|
addr, \
|
|
fbgetpage((caddr_t)page), \
|
|
PROT_READ|PROT_WRITE|PROT_USER, 0)
|
|
|
|
#define CG6UNMAP(segp) \
|
|
hat_unload(segp, \
|
|
(segp)->s_base, \
|
|
(segp)->s_size);
|
|
|
|
/*ARGSUSED*/
|
|
static
|
|
segvx_lockfree(seg, softc)
|
|
struct seg *seg;
|
|
struct vx_softc *softc;
|
|
{
|
|
register struct segvx_data *sdp = (struct segvx_data *)seg->s_data;
|
|
struct seglock *lp;
|
|
static int segvx_timeout();
|
|
|
|
DEBUGprint (0x1, "segvx_lockfree \n", 0, 0);
|
|
|
|
if ((lp = segvx_findlock((off_t)sdp->offset, softc))==NULL)
|
|
return;
|
|
lp->cr.flag |= UNGRAB;
|
|
if (lp->sleepf) {
|
|
untimeout(segvx_timeout, (caddr_t)lp);
|
|
wakeup((caddr_t)lp->page);
|
|
}
|
|
}
|
|
|
|
static
|
|
int
|
|
segvx_timeout(lp)
|
|
struct seglock *lp;
|
|
{
|
|
struct segproc *np;
|
|
void hat_devload();
|
|
|
|
DEBUGprint (0x1, "segvx_timeout \n", 0, 0);
|
|
|
|
np = &lp->cr;
|
|
if (np->flag & LOCKMAP) {
|
|
CG6UNMAP(np->locksegp);
|
|
np->flag &= ~LOCKMAP;
|
|
}
|
|
if (np->flag & UNLOCKMAP)
|
|
CG6UNMAP(np->unlocksegp);
|
|
CG6MAP(np->unlocksegp, np->unlockaddr, lp->page);
|
|
np->flag |= UNLOCKMAP;
|
|
np->flag &= ~TRASHPAGE;
|
|
|
|
np = &lp->cl;
|
|
if (np->locksegp && np->flag & LOCKMAP) {
|
|
CG6UNMAP(np->locksegp);
|
|
np->flag &= ~LOCKMAP;
|
|
}
|
|
if (np->unlocksegp) {
|
|
if (np->flag & UNLOCKMAP)
|
|
CG6UNMAP(np->unlocksegp);
|
|
CG6MAP(np->unlocksegp, np->unlockaddr, vxtrashpage);
|
|
np->flag |= (UNLOCKMAP | TRASHPAGE);
|
|
}
|
|
wakeup((caddr_t)lp->page);
|
|
}
|
|
|
|
/*
|
|
* Handle lock segment faults here...
|
|
*
|
|
*/
|
|
|
|
static
|
|
int
|
|
segvx_lockfault(seg, addr, softc)
|
|
register struct seg *seg;
|
|
addr_t addr;
|
|
struct vx_softc *softc;
|
|
{
|
|
register struct seglock *lp;
|
|
register struct segvx_data *current
|
|
= (struct segvx_data *)seg->s_data;
|
|
struct segproc *sp;
|
|
void hat_devload();
|
|
void hat_unload();
|
|
int s;
|
|
extern int hz;
|
|
|
|
DEBUGprint (0x1, "segvx_lockfault \n", 0, 0);
|
|
|
|
|
|
/* look up the segment in the vxlock_list */
|
|
|
|
lp = segvx_findlock((off_t)current->offset, softc);
|
|
if (lp == NULL)
|
|
return (FC_MAKE_ERR(EFAULT));
|
|
|
|
s = splsoftclock();
|
|
|
|
if (lp->cr.flag & UNGRAB) {
|
|
CG6MAP(seg,addr,vxtrashpage);
|
|
(void)splx(s);
|
|
return(0);
|
|
}
|
|
|
|
/* initialization necessary? */
|
|
|
|
if (lp->cr.procp == u.u_procp && lp->cr.locksegp == NULL) {
|
|
lp->cr.locksegp = seg;
|
|
lp->cr.lockaddr = addr;
|
|
} else if (lp->cr.procp != u.u_procp && lp->cl.locksegp == NULL) {
|
|
lp->cl.procp = u.u_procp;
|
|
lp->cl.locksegp = seg;
|
|
lp->cl.lockaddr = addr;
|
|
} else if (lp->cr.procp == u.u_procp && lp->cr.locksegp != seg) {
|
|
lp->cr.unlocksegp = seg;
|
|
lp->cr.unlockaddr = addr;
|
|
} else if (lp->cl.procp == u.u_procp && lp->cl.locksegp != seg) {
|
|
lp->cl.unlocksegp = seg;
|
|
lp->cl.unlockaddr = addr;
|
|
}
|
|
|
|
if (*(int *)lp->page == 0) {
|
|
if (!lp->sleepf) {
|
|
if (lp->owner == NULL) {
|
|
if (lp->cr.procp == u.u_procp) {
|
|
lp->owner = &lp->cr;
|
|
lp->other = &lp->cl;
|
|
} else {
|
|
lp->owner = &lp->cl;
|
|
lp->other = &lp->cr;
|
|
}
|
|
}
|
|
/* switch ownership? */
|
|
if (lp->other->locksegp == seg) {
|
|
if (lp->owner->flag & LOCKMAP) {
|
|
CG6UNMAP(lp->owner->locksegp);
|
|
lp->owner->flag &= ~LOCKMAP;
|
|
}
|
|
if (lp->owner->unlocksegp != NULL)
|
|
if (lp->owner->flag & UNLOCKMAP) {
|
|
CG6UNMAP(lp->owner->unlocksegp);
|
|
lp->owner->flag &= ~TRASHPAGE;
|
|
lp->owner->flag &= ~UNLOCKMAP;
|
|
}
|
|
sp = lp->owner;
|
|
lp->owner = lp->other;
|
|
lp->other = sp;
|
|
}
|
|
/* map lock segment to page */
|
|
CG6MAP(lp->owner->locksegp, lp->owner->lockaddr, lp->page);
|
|
lp->owner->flag |= LOCKMAP;
|
|
if (lp->owner->unlocksegp != NULL) {
|
|
if (lp->owner->flag & UNLOCKMAP) /***/
|
|
CG6UNMAP(lp->owner->unlocksegp);
|
|
CG6MAP(lp->owner->unlocksegp,
|
|
lp->owner->unlockaddr, lp->page);
|
|
lp->owner->flag &= ~TRASHPAGE;
|
|
lp->owner->flag |= UNLOCKMAP;
|
|
}
|
|
(void)splx(s);
|
|
return (0);
|
|
}
|
|
(void)splx(s);
|
|
return (0);
|
|
}
|
|
|
|
if (*(int *)lp->page == 1) {
|
|
|
|
if (lp->sleepf) {
|
|
if (lp->owner->flag & UNLOCKMAP) /***/
|
|
CG6UNMAP(lp->owner->unlocksegp);
|
|
CG6MAP(lp->owner->unlocksegp, lp->owner->unlockaddr, vxtrashpage);
|
|
lp->owner->flag |= TRASHPAGE;
|
|
lp->owner->flag |= UNLOCKMAP;
|
|
if (lp->owner->flag & LOCKMAP) {
|
|
CG6UNMAP(lp->owner->locksegp);
|
|
lp->owner->flag &= ~LOCKMAP;
|
|
}
|
|
untimeout(segvx_timeout, (caddr_t)lp);
|
|
wakeup((caddr_t)lp->page); /* wake up sleeper */
|
|
(void)splx(s);
|
|
return (0);
|
|
}
|
|
|
|
if (lp->owner->procp==u.u_procp && lp->owner->unlocksegp==seg) {
|
|
if (lp->owner->flag & UNLOCKMAP) /***/
|
|
CG6UNMAP(lp->owner->unlocksegp);
|
|
CG6MAP(lp->owner->unlocksegp, lp->owner->unlockaddr, lp->page);
|
|
lp->owner->flag &= ~TRASHPAGE;
|
|
lp->owner->flag |= UNLOCKMAP;
|
|
(void)splx(s);
|
|
return (0);
|
|
}
|
|
|
|
if (lp->owner->flag & UNLOCKMAP) {
|
|
CG6UNMAP(lp->owner->unlocksegp);
|
|
lp->owner->flag &= ~TRASHPAGE;
|
|
lp->owner->flag &= ~UNLOCKMAP;
|
|
}
|
|
|
|
lp->sleepf = 1;
|
|
|
|
if (lp->cr.procp == u.u_procp) /* creator about to sleep */
|
|
timeout(segvx_timeout, (caddr_t)lp, locktime*hz);
|
|
|
|
(void)sleep((caddr_t)lp->page,PRIBIO); /* goodnight, gracie */
|
|
/* we wake up */
|
|
lp->sleepf = 0; /* clear sleepf */
|
|
if (lp->cr.flag & UNGRAB) {
|
|
CG6MAP(seg,addr,vxtrashpage);
|
|
(void)splx(s);
|
|
return(0);
|
|
}
|
|
|
|
sp = lp->owner; /* switch owner and other */
|
|
lp->owner = lp->other;
|
|
lp->other = sp;
|
|
|
|
/* map new owner to page */
|
|
CG6MAP(lp->owner->locksegp, lp->owner->lockaddr, lp->page);
|
|
lp->owner->flag |= LOCKMAP;
|
|
|
|
if (lp->owner->flag & TRASHPAGE) {
|
|
CG6UNMAP(lp->owner->unlocksegp);
|
|
lp->owner->flag &= ~TRASHPAGE;
|
|
lp->owner->flag |= UNLOCKMAP;
|
|
CG6MAP(lp->owner->unlocksegp, lp->owner->unlockaddr, lp->page);
|
|
}
|
|
}
|
|
(void)splx(s);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Handle a fault on a lego segment. The only tricky part is that only one
|
|
* valid mapping at a time is allowed. Whenever a new mapping is called for, the
|
|
* current values of the TEC and FBC registers in the old context are saved away,
|
|
* and the old values in the new context are restored.
|
|
*/
|
|
|
|
/*ARGSUSED*/
|
|
static
|
|
segvx_fault(seg, addr, len, type, rw)
|
|
register struct seg *seg;
|
|
addr_t addr;
|
|
u_int len;
|
|
enum fault_type type;
|
|
enum seg_rw rw;
|
|
{
|
|
register addr_t adr;
|
|
register struct segvx_data *current = (struct segvx_data *)seg->s_data;
|
|
register struct segvx_data *old;
|
|
int pf;
|
|
register struct vx_softc *softc = getsoftc(minor(current->dev));
|
|
void hat_devload();
|
|
void hat_unload();
|
|
|
|
DEBUGprint (0x1, "segvx_fault \n", 0, 0);
|
|
|
|
if (type != F_INVAL) {
|
|
return (FC_MAKE_ERR(EFAULT));
|
|
}
|
|
|
|
switch(current->flag) {
|
|
case SEG_CG6PRIV: /* cg6 private context */
|
|
case SEG_CG6SHARED: /* cg6 shared context */
|
|
case SEG_VX: /* VX segment (whole vx) */
|
|
case SEG_MISC: /* other cg6/VX bits */
|
|
break;
|
|
case SEG_LOCK: /* lock page */
|
|
return (segvx_lockfault(seg, addr, softc));
|
|
}
|
|
|
|
if (current->cntxtp && softc->curcntxt != seg) {
|
|
/*
|
|
* time to switch lego contexts.
|
|
*/
|
|
if (softc->curcntxt != NULL) {
|
|
old = (struct segvx_data *)softc->curcntxt->s_data;
|
|
/*
|
|
* wipe out old valid mapping.
|
|
*/
|
|
|
|
hat_unload(softc->curcntxt,
|
|
softc->curcntxt->s_base,
|
|
softc->curcntxt->s_size);
|
|
/*
|
|
* switch hardware contexts
|
|
*/
|
|
|
|
if (cgvx_cntxsave(
|
|
(struct fbc *)softc->va[MAP_FBCTEC],
|
|
(struct tec *)(softc->va[MAP_FBCTEC]+CG6_TEC_POFF),
|
|
old->cntxtp) == 0)
|
|
return (FC_HWERR);
|
|
}
|
|
|
|
if (cgvx_cntxrestore(
|
|
(struct fbc *)softc->va[MAP_FBCTEC],
|
|
(struct tec *)(softc->va[MAP_FBCTEC]+CG6_TEC_POFF),
|
|
current->cntxtp) == 0)
|
|
return (FC_HWERR);
|
|
|
|
/*
|
|
* switch software "context"
|
|
*/
|
|
softc->curcntxt = seg;
|
|
}
|
|
for (adr = addr; adr < addr + len; adr += PAGESIZE) {
|
|
pf = cgvxmmap(current->dev, (off_t)current->offset+(adr-seg->s_base),
|
|
PROT_READ|PROT_WRITE);
|
|
if (pf == -1)
|
|
return (FC_MAKE_ERR(EFAULT));
|
|
|
|
hat_devload(seg, adr, pf, PROT_READ|PROT_WRITE|PROT_USER, 0 /* don't lock */);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static
|
|
segvx_checkprot(seg, addr, len, prot)
|
|
struct seg *seg;
|
|
addr_t addr;
|
|
u_int len, prot;
|
|
{
|
|
DEBUGprint (0x1, "segvx_checkprot \n", 0, 0);
|
|
|
|
return (PROT_READ|PROT_WRITE);
|
|
}
|
|
|
|
|
|
/*
|
|
* segdev pages are always "in core".
|
|
*/
|
|
/*ARGSUSED*/
|
|
static
|
|
segvx_incore(seg, addr, len, vec)
|
|
struct seg *seg;
|
|
addr_t addr;
|
|
register u_int len;
|
|
register char *vec;
|
|
{
|
|
u_int v = 0;
|
|
|
|
DEBUGprint (0x1, "segvx_incore \n", 0, 0);
|
|
|
|
for (len = (len + PAGEOFFSET) & PAGEMASK; len; len -= PAGESIZE,
|
|
v += PAGESIZE)
|
|
*vec++ = 1;
|
|
return (v);
|
|
}
|
|
|
|
|
|
static
|
|
segvx_badop()
|
|
{
|
|
DEBUGprint (0x1, "segvx_badop \n", 0, 0);
|
|
|
|
/*
|
|
* silently fail.
|
|
*/
|
|
return (0);
|
|
}
|
|
|
|
#undef L_TEC_VDC_INTRNL0
|
|
#define L_TEC_VDC_INTRNL0 0x8000
|
|
#undef L_TEC_VDC_INTRNL1
|
|
#define L_TEC_VDC_INTRNL1 0xa000
|
|
|
|
|
|
cgvx_cntxsave(fbc, tec, saved)
|
|
struct fbc *fbc;
|
|
struct tec *tec;
|
|
struct cgvx_cntxt *saved;
|
|
{
|
|
int dreg; /* counts through the data registers */
|
|
u_int *dp; /* points to a tec data register */
|
|
register spin;
|
|
|
|
DEBUGprint (0x1, "cgvx_cntxsave \n", 0, 0);
|
|
|
|
/*
|
|
* wait for fbc not busy; but not forever
|
|
*/
|
|
for (spin = 100000; fbc->l_fbc_status & L_FBC_BUSY && --spin > 0;)
|
|
;
|
|
if (spin == 0) {
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* start dumping stuff out.
|
|
*/
|
|
saved->fbc.status = fbc->l_fbc_status;
|
|
saved->fbc.clipcheck = fbc->l_fbc_clipcheck;
|
|
saved->fbc.misc = fbc->l_fbc_misc;
|
|
saved->fbc.x0 = fbc->l_fbc_x0;
|
|
saved->fbc.y0 = fbc->l_fbc_y0;
|
|
saved->fbc.x1 = fbc->l_fbc_x1;
|
|
saved->fbc.y1 = fbc->l_fbc_y1;
|
|
saved->fbc.x2 = fbc->l_fbc_x2;
|
|
saved->fbc.y2 = fbc->l_fbc_y2;
|
|
saved->fbc.x3 = fbc->l_fbc_x3;
|
|
saved->fbc.y3 = fbc->l_fbc_y3;
|
|
saved->fbc.rasteroffx = fbc->l_fbc_rasteroffx;
|
|
saved->fbc.rasteroffy = fbc->l_fbc_rasteroffy;
|
|
saved->fbc.autoincx = fbc->l_fbc_autoincx;
|
|
saved->fbc.autoincy = fbc->l_fbc_autoincy;
|
|
saved->fbc.clipminx = fbc->l_fbc_clipminx;
|
|
saved->fbc.clipminy = fbc->l_fbc_clipminy;
|
|
saved->fbc.clipmaxx = fbc->l_fbc_clipmaxx;
|
|
saved->fbc.clipmaxy = fbc->l_fbc_clipmaxy;
|
|
saved->fbc.fcolor = fbc->l_fbc_fcolor;
|
|
saved->fbc.bcolor = fbc->l_fbc_bcolor;
|
|
saved->fbc.rasterop = fbc->l_fbc_rasterop;
|
|
saved->fbc.planemask = fbc->l_fbc_planemask;
|
|
saved->fbc.pixelmask = fbc->l_fbc_pixelmask;
|
|
saved->fbc.pattalign = fbc->l_fbc_pattalign;
|
|
saved->fbc.pattern0 = fbc->l_fbc_pattern0;
|
|
saved->fbc.pattern1 = fbc->l_fbc_pattern1;
|
|
saved->fbc.pattern2 = fbc->l_fbc_pattern2;
|
|
saved->fbc.pattern3 = fbc->l_fbc_pattern3;
|
|
saved->fbc.pattern4 = fbc->l_fbc_pattern4;
|
|
saved->fbc.pattern5 = fbc->l_fbc_pattern5;
|
|
saved->fbc.pattern6 = fbc->l_fbc_pattern6;
|
|
saved->fbc.pattern7 = fbc->l_fbc_pattern7;
|
|
|
|
/*
|
|
* the tec matrix and clipping registers are easy.
|
|
*/
|
|
saved->tec.mv = tec->l_tec_mv;
|
|
saved->tec.clip = tec->l_tec_clip;
|
|
saved->tec.vdc = tec->l_tec_vdc;
|
|
|
|
/*
|
|
* the tec data registers are a little more non-obvious.
|
|
* internally, they are 36 bits. what we see in the
|
|
* register file is a 32-bit window onto the underlying
|
|
* data register. changing the data-type in the VDC
|
|
* gets us either of two parts of the data register.
|
|
* the internal format is opaque to us.
|
|
*/
|
|
tec->l_tec_vdc = (u_int)L_TEC_VDC_INTRNL0;
|
|
for (dreg = 0, dp = &tec->l_tec_data00; dreg < 64; dreg++, dp++) {
|
|
saved->tec.data[dreg][0] = *dp;
|
|
}
|
|
tec->l_tec_vdc = (u_int)L_TEC_VDC_INTRNL1;
|
|
for (dreg = 0, dp = &tec->l_tec_data00; dreg < 64; dreg++, dp++) {
|
|
saved->tec.data[dreg][1] = *dp;
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
cgvx_cntxrestore(fbc, tec, saved)
|
|
struct fbc *fbc;
|
|
struct tec *tec;
|
|
struct cgvx_cntxt *saved;
|
|
{
|
|
int dreg;
|
|
u_int *dp;
|
|
register spin;
|
|
|
|
DEBUGprint (0x1, "cgvx_cntxrestore \n", 0, 0);
|
|
|
|
/*
|
|
* wait for fbc not busy; but not forever
|
|
*/
|
|
for (spin = 100000; fbc->l_fbc_status & L_FBC_BUSY && --spin > 0;)
|
|
;
|
|
if (spin == 0) {
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* reload the tec data registers. see above for
|
|
* "how do they get 36 bits in that itty-bitty int"
|
|
*/
|
|
tec->l_tec_vdc = (u_int)L_TEC_VDC_INTRNL0;
|
|
for (dreg = 0, dp = &tec->l_tec_data00; dreg < 64; dreg++, dp++) {
|
|
*dp = saved->tec.data[dreg][0];
|
|
}
|
|
tec->l_tec_vdc = (u_int)L_TEC_VDC_INTRNL1;
|
|
for (dreg = 0, dp = &tec->l_tec_data00; dreg < 64; dreg++, dp++) {
|
|
*dp = saved->tec.data[dreg][1];
|
|
}
|
|
|
|
/*
|
|
* the tec matrix and clipping registers are next.
|
|
*/
|
|
tec->l_tec_mv = saved->tec.mv;
|
|
tec->l_tec_clip = saved->tec.clip;
|
|
tec->l_tec_vdc = saved->tec.vdc;
|
|
|
|
/*
|
|
* now the FBC vertex and address registers
|
|
*/
|
|
fbc->l_fbc_x0 = saved->fbc.x0;
|
|
fbc->l_fbc_y0 = saved->fbc.y0;
|
|
fbc->l_fbc_x1 = saved->fbc.x1;
|
|
fbc->l_fbc_y1 = saved->fbc.y1;
|
|
fbc->l_fbc_x2 = saved->fbc.x2;
|
|
fbc->l_fbc_y2 = saved->fbc.y2;
|
|
fbc->l_fbc_x3 = saved->fbc.x3;
|
|
fbc->l_fbc_y3 = saved->fbc.y3;
|
|
fbc->l_fbc_rasteroffx = saved->fbc.rasteroffx;
|
|
fbc->l_fbc_rasteroffy = saved->fbc.rasteroffy;
|
|
fbc->l_fbc_autoincx = saved->fbc.autoincx;
|
|
fbc->l_fbc_autoincy = saved->fbc.autoincy;
|
|
fbc->l_fbc_clipminx = saved->fbc.clipminx;
|
|
fbc->l_fbc_clipminy = saved->fbc.clipminy;
|
|
fbc->l_fbc_clipmaxx = saved->fbc.clipmaxx;
|
|
fbc->l_fbc_clipmaxy = saved->fbc.clipmaxy;
|
|
|
|
/*
|
|
* restoring the attribute registers
|
|
*/
|
|
fbc->l_fbc_fcolor = saved->fbc.fcolor;
|
|
fbc->l_fbc_bcolor = saved->fbc.bcolor;
|
|
fbc->l_fbc_rasterop = saved->fbc.rasterop;
|
|
fbc->l_fbc_planemask = saved->fbc.planemask;
|
|
fbc->l_fbc_pixelmask = saved->fbc.pixelmask;
|
|
fbc->l_fbc_pattalign = saved->fbc.pattalign;
|
|
fbc->l_fbc_pattern0 = saved->fbc.pattern0;
|
|
fbc->l_fbc_pattern1 = saved->fbc.pattern1;
|
|
fbc->l_fbc_pattern2 = saved->fbc.pattern2;
|
|
fbc->l_fbc_pattern3 = saved->fbc.pattern3;
|
|
fbc->l_fbc_pattern4 = saved->fbc.pattern4;
|
|
fbc->l_fbc_pattern5 = saved->fbc.pattern5;
|
|
fbc->l_fbc_pattern6 = saved->fbc.pattern6;
|
|
fbc->l_fbc_pattern7 = saved->fbc.pattern7;
|
|
|
|
fbc->l_fbc_clipcheck = saved->fbc.clipcheck;
|
|
fbc->l_fbc_misc = saved->fbc.misc;
|
|
|
|
/*
|
|
* lastly, let's restore the status
|
|
*/
|
|
fbc->l_fbc_status = saved->fbc.status;
|
|
return (1);
|
|
}
|