2716 lines
66 KiB
C
2716 lines
66 KiB
C
#if !defined(lint) && !defined(NOID)
|
|
static char sccsid[] = "@(#)@(#)cgsix.c 1.1 92/07/30";
|
|
#endif
|
|
|
|
/*
|
|
* Copyright 1989, Sun Microsystems, Inc.
|
|
*/
|
|
|
|
/*
|
|
* Sbus accelerated 8 bit color frame buffer driver
|
|
*/
|
|
|
|
#include "win.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/time.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/map.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/user.h>
|
|
#include <sys/vmmac.h>
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/kmem_alloc.h>
|
|
|
|
#include <sun/fbio.h>
|
|
|
|
#include <sundev/mbvar.h>
|
|
#include <sundev/cg6reg.h>
|
|
|
|
#include <machine/mmu.h>
|
|
#include <machine/pte.h>
|
|
|
|
#include <pixrect/pixrect.h>
|
|
#include <pixrect/pr_impl_util.h>
|
|
#include <pixrect/pr_planegroups.h>
|
|
#include <pixrect/memvar.h>
|
|
#include <pixrect/cg3var.h> /* for CG3_MMAP_OFFSET */
|
|
#include <pixrect/cg6var.h>
|
|
|
|
#include <machine/vm_hat.h>
|
|
#include <vm/as.h>
|
|
#include <vm/seg.h>
|
|
|
|
addr_t map_regs();
|
|
void unmap_regs();
|
|
void report_dev();
|
|
|
|
/*
|
|
* This flag indicates that we should use kcopy instead of copyin
|
|
* for cursor ioctls.
|
|
*/
|
|
int fbio_kcopy;
|
|
extern int copyin(), kcopy();
|
|
|
|
#define CG6_FBC_WAIT 500000 /* .5 seconds */
|
|
|
|
/*
|
|
* A bunch of places in here need to know whether
|
|
* we are supporting the "P4" or the "SBus" version.
|
|
*/
|
|
#if defined(sun4c) || defined(sun4m)
|
|
#define SBUS_CGSIX
|
|
#endif
|
|
|
|
/* configuration options */
|
|
#ifndef OLDDEVSW
|
|
#define OLDDEVSW 1
|
|
#endif OLDDEVSW
|
|
|
|
#ifndef NOHWINIT
|
|
#define NOHWINIT 0
|
|
#endif NOHWINIT
|
|
|
|
#ifndef CG6DEBUG
|
|
#define CG6DEBUG 1
|
|
#endif CG6DEBUG
|
|
|
|
#if NOHWINIT
|
|
int cg6_hwinit = 1;
|
|
#endif NOHWINIT
|
|
|
|
#if CG6DEBUG
|
|
int cg6_debug = 0;
|
|
#define DEBUGF(level, args) _STMT(if (cg6_debug >= (level)) printf args;)
|
|
#define DUMP_SEGS(level, c) _STMT(if (cg6_debug >= (level)) dump_segs(c);)
|
|
#else CG6DEBUG
|
|
#define DEBUGF(level, args) /* nothing */
|
|
#define DUMP_SEGS(level, c) /* nothing */
|
|
#endif CG6DEBUG
|
|
|
|
#if OLDDEVSW
|
|
#define STATIC /* nothing */
|
|
#define cg6_open cgsixopen
|
|
#define cg6_close cgsixclose
|
|
#define cg6_mmap cgsixmmap
|
|
#define cg6_ioctl cgsixioctl
|
|
#else OLDDEVSW
|
|
#define STATIC static
|
|
#endif OLDDEVSW
|
|
|
|
typedef int (*ifunc)() ;
|
|
|
|
/* memory-leak testing */
|
|
|
|
#if CG6DEBUG
|
|
static int total_memory = 0 ;
|
|
|
|
static caddr_t
|
|
my_kmem_zalloc(size,flag)
|
|
u_int size;
|
|
int flag;
|
|
{
|
|
total_memory += size ;
|
|
DEBUGF(5, ("zalloc %d bytes, total=%d\n",size, total_memory));
|
|
return new_kmem_zalloc(size,flag) ;
|
|
}
|
|
|
|
static caddr_t
|
|
my_kmem_alloc(size,flag)
|
|
u_int size;
|
|
int flag;
|
|
{
|
|
total_memory += size ;
|
|
DEBUGF(5, ("alloc %d bytes, total=%d\n",size, total_memory));
|
|
return new_kmem_alloc(size,flag) ;
|
|
}
|
|
|
|
static
|
|
my_kmem_free(ptr,size)
|
|
caddr_t ptr;
|
|
u_int size;
|
|
{
|
|
total_memory -= size ;
|
|
DEBUGF(5, ("free %d bytes, total=%d\n",size, total_memory));
|
|
kmem_free(ptr, size) ;
|
|
}
|
|
|
|
#define new_kmem_zalloc my_kmem_zalloc
|
|
#define new_kmem_alloc my_kmem_alloc
|
|
#define kmem_free my_kmem_free
|
|
|
|
#endif CG6DEBUG
|
|
|
|
|
|
|
|
/* config info */
|
|
static int cg6_identify();
|
|
static int cg6_attach();
|
|
STATIC int cg6_open();
|
|
STATIC int cg6_close();
|
|
STATIC int cg6_ioctl();
|
|
STATIC int cgsixsegmap();
|
|
|
|
struct dev_ops cgsix_ops = {
|
|
0, /* revision */
|
|
cg6_identify,
|
|
cg6_attach,
|
|
cg6_open,
|
|
cg6_close,
|
|
0, 0, 0, 0, 0,
|
|
cg6_ioctl,
|
|
0,
|
|
cgsixsegmap,
|
|
};
|
|
|
|
/*
|
|
* Table of CG6 addresses
|
|
*/
|
|
static struct mapping {
|
|
int vaddr; /* virtual (mmap offset) address */
|
|
int paddr; /* physical (byte) address */
|
|
int size; /* size in bytes (page multiple) */
|
|
} cg6_map[] = {
|
|
{ CG6_VADDR_FBCTEC, CG6_ADDR_FBC, CG6_FBCTEC_SZ },
|
|
{ CG6_VADDR_CMAP, CG6_ADDR_CMAP, CG6_CMAP_SZ },
|
|
{ CG6_VADDR_FHCTHC, CG6_ADDR_FHCTHC, CG6_FHCTHC_SZ },
|
|
{ CG6_VADDR_ROM, CG6_ADDR_ROM, CG6_ROM_SZ },
|
|
{ CG6_VADDR_DHC, CG6_ADDR_DHC, CG6_DHC_SZ },
|
|
{ CG6_VADDR_ALT, CG6_ADDR_ALT, CG6_ALT_SZ },
|
|
{ 0 }
|
|
};
|
|
|
|
#define MAP_FBCTEC 0
|
|
|
|
/* how much to map */
|
|
#define CG6MAPSIZE MMAPSIZE(0)
|
|
|
|
/* vertical retrace counter page */
|
|
#define VRT_OFFSET (CG6_VADDR_COLOR+CG6_FB_SZ)
|
|
|
|
/* per-unit data */
|
|
struct cg6_softc
|
|
{
|
|
#if NWIN > 0
|
|
Pixrect pr; /* kernel pixrect */
|
|
struct mprp_data prd; /* pixrect private data */
|
|
#define _w pr.pr_size.x
|
|
#define _h pr.pr_size.y
|
|
#define _fb prd.mpr.md_image
|
|
#define _linebytes prd.mpr.md_linebytes
|
|
#else NWIN > 0
|
|
int _w, _h; /* resolution */
|
|
MPR_T *_fb; /* frame buffer address */
|
|
#endif NWIN > 0
|
|
int size; /* total size of frame buffer */
|
|
int dummysize; /* total size of overlay plane */
|
|
addr_t base; /* mapped base address */
|
|
int basepage; /* page # for base address */
|
|
|
|
struct softcur {
|
|
short enable; /* cursor enable */
|
|
short pad1;
|
|
struct fbcurpos pos; /* cursor position */
|
|
struct fbcurpos hot; /* cursor hot spot */
|
|
struct fbcurpos size; /* cursor bitmap size */
|
|
u_long image[32]; /* cursor image bitmap */
|
|
u_long mask[32]; /* cursor mask bitmap */
|
|
} cur;
|
|
|
|
union { /* shadow overlay color map */
|
|
u_long omap_long[2]; /* cheating here to save space */
|
|
u_char omap_char[3][2];
|
|
} omap_image;
|
|
#define omap_rgb omap_image.omap_char[0]
|
|
u_short omap_update; /* overlay colormap update flag */
|
|
|
|
u_short cmap_index; /* colormap update index */
|
|
u_short cmap_count; /* colormap update count */
|
|
union { /* shadow color map */
|
|
u_long cmap_long[CG6_CMAP_ENTRIES * 3 / sizeof (u_long)];
|
|
u_char cmap_char[3][CG6_CMAP_ENTRIES];
|
|
} cmap_image;
|
|
#define cmap_rgb cmap_image.cmap_char[0]
|
|
|
|
#define CG6VRTIOCTL 1 /* FBIOVERTICAL in effect */
|
|
#define CG6VRTCTR 2 /* OWGX vertical retrace counter */
|
|
unsigned long fbmappable; /* bytes mappable */
|
|
int *vrtpage; /* pointer to VRT page */
|
|
int vrtmaps; /* number of VRT page maps */
|
|
int vrtflag; /* vrt interrupt flag */
|
|
struct cg6_info cg6info; /* info about this cg6 */
|
|
struct mon_info moninfo; /* info about this monitor */
|
|
struct cg6_cntxt *curcntxt; /* context switching */
|
|
int chiprev ; /* fbc chip revision # */
|
|
#ifdef MULTIPROCESSOR
|
|
struct segcg6_data *mappings ; /* full list of mappings */
|
|
#endif MULTIPROCESSOR
|
|
};
|
|
|
|
/* Per-segment info:
|
|
* Some, but not all, segments are part of a context.
|
|
* Any segment that is a MAP_PRIVATE mapping to the TEC or FBC
|
|
* will be part of a unique context. MAP_SHARED mappings are part
|
|
* of the shared context and all such programs must arbitrate among
|
|
* themselves to keep from stepping on each other's register settings.
|
|
* Mappings to the framebuffer may or may not be part of a context,
|
|
* depending on exact hardware type.
|
|
*/
|
|
struct segcg6_data {
|
|
struct seg *link; /* next segment in ctx */
|
|
struct seg *seg; /* pointer to segment */
|
|
int flag; /* see below */
|
|
struct cg6_cntxt *cntxtp; /* pointer to context */
|
|
dev_t dev;
|
|
u_int offset;
|
|
u_char prot, maxprot ;
|
|
#ifdef MULTIPROCESSOR
|
|
struct segcg6_data *next_map ; /* link to next mapping */
|
|
#endif MULTIPROCESSOR
|
|
};
|
|
|
|
#define SEG_CTXINIT (1) /* part of a context */
|
|
#define SEG_SHARED (2) /* shared context */
|
|
#define SEG_LOCK (4) /* lock page */
|
|
#define SEG_VRT (8) /* vrt page */
|
|
#define SEG_FBC (0X10) /* segment includes fbc */
|
|
#define SEG_TEC (0X40) /* segment includes tec */
|
|
#define SEG_FB (0X100) /* segment includes framebuffer */
|
|
#define SEG_CMAP (0X400) /* segment includes dacs */
|
|
|
|
#define SEG_GLOBAL_MAP (SEG_CMAP) /* segs that need to be in map list */
|
|
|
|
|
|
static int ncg6;
|
|
static struct cg6_softc *cg6_softc = NULL ;
|
|
|
|
/* default structure for FBIOGATTR ioctl */
|
|
static struct fbgattr cg6_attr = {
|
|
/* 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 } },
|
|
/* emu_types */
|
|
{ FBTYPE_SUNFAST_COLOR, FBTYPE_SUN3COLOR, FBTYPE_SUN4COLOR, -1}
|
|
};
|
|
|
|
|
|
/*
|
|
* handy macros
|
|
*/
|
|
|
|
#define getsoftc(unit) (&cg6_softc[unit])
|
|
|
|
#define btob(n) ctob(btoc(n)) /* XXX */
|
|
|
|
#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 CG6_CURSOR_OFFPOS (0xffe0ffe0)
|
|
|
|
#define cg6_set_video(softc, on) \
|
|
thc_set_video(CG6VA_TO_THC((softc)->base), (on))
|
|
|
|
#define cg6_get_video(softc) \
|
|
thc_get_video(CG6VA_TO_THC((softc)->base))
|
|
|
|
#define cg6_int_enable(softc) \
|
|
thc_int_enable(CG6VA_TO_THC((softc)->base))
|
|
|
|
#define cg6_int_disable(softc) \
|
|
thc_int_disable(CG6VA_TO_THC((softc)->base))
|
|
|
|
#define cg6_int_pending(softc) \
|
|
thc_int_pending(CG6VA_TO_THC((softc)->base))
|
|
|
|
/* check if color map update is pending */
|
|
#define cg6_update_pending(softc) \
|
|
((softc)->cmap_count || (softc)->omap_update)
|
|
|
|
|
|
/*
|
|
* forward references
|
|
*/
|
|
static cg6_poll();
|
|
static void cg6_intr();
|
|
static void cg6_reset_cmap();
|
|
static void cg6_update_cmap();
|
|
static void cg6_cmap_bcopy();
|
|
|
|
#if NWIN > 0
|
|
|
|
/*
|
|
* SunWindows specific stuff
|
|
*/
|
|
static cg6_rop(), cg6_putcolormap();
|
|
|
|
/* kernel pixrect ops vector */
|
|
static struct pixrectops cg6_pr_ops = {
|
|
cg6_rop,
|
|
cg6_putcolormap,
|
|
mem_putattributes
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* XXX cursor turd avoidance -- there must be a better way to do this */
|
|
|
|
static cg6_rop_wait = 50; /* milliseconds */
|
|
|
|
static
|
|
cg6_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;
|
|
|
|
if (spr && spr->pr_ops == &cg6_pr_ops) {
|
|
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 =
|
|
&CG6VA_TO_FBC((getsoftc(unit)->base))->l_fbc_status;
|
|
|
|
CDELAY(!(*statp & L_FBC_BUSY), cg6_rop_wait << 10);
|
|
}
|
|
|
|
return mem_rop(dpr, dx, dy, dw, dh, op, spr, sx, sy);
|
|
}
|
|
|
|
/* XXX can I share code with ioctl? */
|
|
/* XXX vestigial overlay stuff */
|
|
static
|
|
cg6_putcolormap(pr, index, count, red, green, blue)
|
|
Pixrect *pr;
|
|
int index, count;
|
|
unsigned char *red, *green, *blue;
|
|
{
|
|
register struct cg6_softc *softc = getsoftc(mpr_d(pr)->md_primary);
|
|
register u_int rindex = (u_int) index;
|
|
register u_int rcount = (u_int) count;
|
|
register u_char *map;
|
|
register u_int entries;
|
|
|
|
DEBUGF(5, ("cg6_putcolormap unit=%d index=%d count=%d\n",
|
|
mpr_d(pr)->md_primary, index, count));
|
|
|
|
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 (cg6_update_pending(softc))
|
|
cg6_int_disable(softc);
|
|
|
|
map += rindex * 3;
|
|
cg6_cmap_bcopy(red, map, rcount);
|
|
cg6_cmap_bcopy(green, map + 1, rcount);
|
|
cg6_cmap_bcopy(blue, map + 2, rcount);
|
|
|
|
cg6_update_cmap(softc, rindex, rcount);
|
|
|
|
/* enable interrupt so we can load the hardware colormap */
|
|
cg6_int_enable(softc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif NWIN > 0
|
|
|
|
static
|
|
cg6_identify(name)
|
|
char *name;
|
|
{
|
|
DEBUGF(1, ("cg6_identify(%s)\n", name));
|
|
|
|
if (strcmp(name, "cgsix") == 0)
|
|
return ++ncg6;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
cg6_attach(devi)
|
|
struct dev_info *devi;
|
|
{
|
|
register struct cg6_softc *softc;
|
|
register addr_t reg;
|
|
int w, h, bytes, basepage;
|
|
static int unit;
|
|
extern struct dev_info *top_devinfo; /* in autoconf.c */
|
|
|
|
DEBUGF(1, ("cg6_attach ncg6=%d unit=%d\n", ncg6, unit));
|
|
|
|
/* Allocate softc structs on first attach */
|
|
if (!cg6_softc) {
|
|
cg6_softc = (struct cg6_softc *)
|
|
new_kmem_zalloc((u_int) sizeof (struct cg6_softc) * ncg6,1);
|
|
unit = 0;
|
|
}
|
|
|
|
softc = getsoftc(unit);
|
|
|
|
/* Grab properties from PROM */
|
|
/* XXX don't really want default w, h */
|
|
softc->_w = w = getprop(devi->devi_nodeid, "width", 1152);
|
|
softc->_h = h = getprop(devi->devi_nodeid, "height", 900);
|
|
bytes = getprop(devi->devi_nodeid, "linebytes", mpr_linebytes(w, 8));
|
|
#if NWIN > 0
|
|
softc->_linebytes = bytes;
|
|
#endif NWIN > 0
|
|
|
|
/* Compute size of color frame buffer */
|
|
bytes = btob(bytes * h);
|
|
softc->size = CG6MAPSIZE + bytes;
|
|
|
|
/* Compute size of dummy overlay/enable planes */
|
|
softc->dummysize = btob(mpr_linebytes(w, 1) * h) * 2;
|
|
|
|
#if NWIN > 0
|
|
/* only use address property if we are console fb */
|
|
if (reg = (addr_t) getprop(devi->devi_nodeid, "address", 0)) {
|
|
int fbid = getprop(top_devinfo->devi_nodeid, "fb", -1);
|
|
if (fbid == devi->devi_nodeid) {
|
|
softc->_fb = (MPR_T *) reg;
|
|
bytes = 0;
|
|
DEBUGF(2, ("cg6 mapped by PROM\n"));
|
|
}
|
|
}
|
|
#else NWIN > 0
|
|
bytes = 0;
|
|
#endif NWIN > 0
|
|
softc->cg6info.line_bytes = softc->_linebytes;
|
|
softc->cg6info.vmsize = getprop(devi->devi_nodeid, "vmsize", 1);
|
|
if (softc->cg6info.vmsize > 1) {
|
|
softc->size = CG6MAPSIZE+8*1024*1024;
|
|
softc->fbmappable = 8*1024*1024;
|
|
} else
|
|
softc->fbmappable = 1024*1024;
|
|
softc->cg6info.accessible_width =
|
|
getprop(devi->devi_nodeid, "awidth", 1152);
|
|
softc->cg6info.accessible_height =
|
|
(softc->cg6info.vmsize*1024*1024)/softc->cg6info.accessible_width;
|
|
softc->cg6info.hdb_capable =
|
|
getprop(devi->devi_nodeid, "dblbuf", 0);
|
|
softc->cg6info.boardrev = getprop(devi->devi_nodeid, "boardrev", 0);
|
|
#ifdef SBUS_SIZE
|
|
softc->cg6info.slot =
|
|
((int) devi->devi_reg[0].reg_addr - SBUS_BASE) / SBUS_SIZE ;
|
|
#else
|
|
softc->cg6info.slot = sbusslot((unsigned) devi->devi_reg[0].reg_addr);
|
|
#endif
|
|
softc->vrtpage = NULL;
|
|
softc->vrtmaps = 0;
|
|
softc->vrtflag = 0;
|
|
#define DEBUG
|
|
#ifdef DEBUG
|
|
softc->cg6info.pad1 = CG6_VADDR_COLOR+CG6_FB_SZ;
|
|
#endif
|
|
/*
|
|
* get monitor attributes
|
|
*/
|
|
softc->moninfo.mon_type = getprop(devi->devi_nodeid, "montype", 0);
|
|
softc->moninfo.pixfreq = getprop(devi->devi_nodeid, "pixfreq", 929405);
|
|
softc->moninfo.hfreq = getprop(devi->devi_nodeid, "hfreq", 61795);
|
|
softc->moninfo.vfreq = getprop(devi->devi_nodeid, "vfreq", 65690);
|
|
softc->moninfo.hfporch = getprop(devi->devi_nodeid, "hfporch", 32);
|
|
softc->moninfo.vfporch = getprop(devi->devi_nodeid, "vfporch", 2);
|
|
softc->moninfo.hbporch = getprop(devi->devi_nodeid, "hbporch", 192);
|
|
softc->moninfo.vbporch = getprop(devi->devi_nodeid, "vbporch", 31);
|
|
softc->moninfo.hsync = getprop(devi->devi_nodeid, "hsync", 128);
|
|
softc->moninfo.vsync = getprop(devi->devi_nodeid, "vsync", 4);
|
|
|
|
|
|
/*
|
|
* Allocate contiguous virtual space for registers and
|
|
* maybe frame buffer. This gets overlayed a few lines down
|
|
* by fbmapin. Kind of a retro-kludge; fixed in SVr4.
|
|
*/
|
|
if (!(reg = map_regs(devi->devi_reg[0].reg_addr,
|
|
(u_int) (CG6MAPSIZE + bytes),
|
|
devi->devi_reg[0].reg_bustype)))
|
|
return -1;
|
|
|
|
softc->base = reg;
|
|
softc->basepage = basepage = fbgetpage(reg);
|
|
softc->curcntxt = NULL ;
|
|
#ifdef MULTIPROCESSOR
|
|
softc->mappings = NULL ;
|
|
#endif MULTIPROCESSOR
|
|
|
|
/* next, replace this register mapping new ones. Our
|
|
* offsets are based on the virtual addresses in cg6reg.h
|
|
*/
|
|
|
|
#define FBMAPIN(v,p,s) fbmapin((addr_t)(reg + v - CG6_VBASE), \
|
|
(int)(basepage + btoc(p)), (int)s)
|
|
|
|
FBMAPIN(CG6_VADDR_FBCTEC, CG6_ADDR_FBC, CG6_FBCTEC_SZ) ;
|
|
FBMAPIN(CG6_VADDR_CMAP, CG6_ADDR_CMAP, CG6_CMAP_SZ) ;
|
|
FBMAPIN(CG6_VADDR_FHCTHC, CG6_ADDR_FHCTHC, CG6_FHCTHC_SZ) ;
|
|
FBMAPIN(CG6_VADDR_ROM, CG6_ADDR_ROM, CG6_ROM_SZ) ;
|
|
|
|
|
|
#if NWIN > 0
|
|
/* map frame buffer if necessary */
|
|
if (bytes) {
|
|
fbmapin((addr_t) CG6VA_TO_DFB(reg),
|
|
(int) (basepage + btoc(CG6_ADDR_COLOR)), bytes);
|
|
softc->_fb = CG6VA_TO_DFB(reg);
|
|
}
|
|
#endif NWIN > 0
|
|
|
|
softc->chiprev =
|
|
(*CG6VA_TO_FHC(reg) >> FHC_CONFIG_REV_SHIFT) & FHC_CONFIG_REV_MASK ;
|
|
|
|
DEBUGF(1, ("cg6_attach reg=0x%x/0x%x (0x%x)\n",
|
|
(u_int) reg,
|
|
fbgetpage((addr_t) reg) << PGSHIFT,
|
|
fbgetpage((addr_t) reg)));
|
|
|
|
#if NOHWINIT
|
|
if (cg6_hwinit)
|
|
cg6_init(reg);
|
|
#endif NOHWINIT
|
|
cg6_reset(reg);
|
|
|
|
/* save unit number */
|
|
devi->devi_unit = unit;
|
|
|
|
/* Notify VM system our interrupt handler calls the hat layer. */
|
|
adddma(devi->devi_intr[0].int_pri);
|
|
|
|
/* attach interrupt */
|
|
addintr(devi->devi_intr[0].int_pri, cg6_poll, devi->devi_name, unit);
|
|
|
|
/* save back pointer to softc */
|
|
devi->devi_data = (addr_t) softc;
|
|
|
|
/*
|
|
* Initialize hardware colormap and software colormap images.
|
|
* It might make sense to read the hardware colormap here.
|
|
*/
|
|
cg6_reset_cmap(softc->cmap_rgb, CG6_CMAP_ENTRIES);
|
|
cg6_reset_cmap(softc->omap_rgb, 2);
|
|
cg6_update_cmap(softc, (u_int) _ZERO_, CG6_CMAP_ENTRIES);
|
|
cg6_update_cmap(softc, (u_int) _ZERO_, (u_int) _ZERO_);
|
|
|
|
report_dev(devi);
|
|
|
|
if ((* CG6VA_TO_FHC(reg) >> FHC_CONFIG_REV_SHIFT &
|
|
FHC_CONFIG_REV_MASK) == 0)
|
|
printf("Revision 0 FBC\n");
|
|
|
|
printf("cgsix%d: screen %dx%d, %s buffered, %dM mappable, rev %d\n",
|
|
unit, w,h, softc->cg6info.hdb_capable ? "double" : "single",
|
|
softc->cg6info.vmsize, softc->chiprev ) ;
|
|
|
|
/* prepare for next attach */
|
|
unit++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
STATIC
|
|
cg6_open(dev, flag)
|
|
dev_t dev;
|
|
int flag;
|
|
{
|
|
DEBUGF(2, ("cg6_open(%d), mem used=%d\n", minor(dev), total_memory));
|
|
|
|
return fbopen(dev, flag, ncg6);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
STATIC
|
|
cg6_close(dev, flag)
|
|
dev_t dev;
|
|
int flag;
|
|
{
|
|
struct cg6_softc *softc = getsoftc(minor(dev));
|
|
|
|
DEBUGF(2, ("cg6_close(%d), mem used=%d\n", minor(dev), total_memory));
|
|
|
|
softc->cur.enable = 0;
|
|
softc->curcntxt = NULL ;
|
|
cg6_reset(softc->base);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
STATIC
|
|
cg6_mmap(dev, off, prot)
|
|
dev_t dev;
|
|
register off_t off;
|
|
int prot;
|
|
{
|
|
register struct cg6_softc *softc = getsoftc(minor(dev));
|
|
register int page = -1;
|
|
|
|
DEBUGF(off ? 4 : 1, ("cg6_mmap(%d, 0x%x)\n", minor(dev), (u_int) off));
|
|
|
|
if ((u_int) off < CG6_VBASE) {
|
|
if (off >= CG3_MMAP_OFFSET)
|
|
off += CG6_VADDR_COLOR - CG3_MMAP_OFFSET;
|
|
else if ((off -= softc->dummysize) < 0)
|
|
off = CG6_VADDR_ROM;
|
|
else
|
|
off += CG6_VADDR_COLOR;
|
|
}
|
|
|
|
if ((u_int) (off - CG6_VADDR_COLOR) < softc->fbmappable)
|
|
page = CG6_ADDR_COLOR + (off - CG6_VADDR_COLOR);
|
|
else {
|
|
register struct mapping *mp;
|
|
|
|
for (mp = cg6_map; mp->size; mp++)
|
|
if ((u_int) (off - mp->vaddr) < mp->size)
|
|
page = mp->paddr + (off - mp->vaddr);
|
|
}
|
|
|
|
if (page >= 0)
|
|
page = softc->basepage + btop(page);
|
|
|
|
DEBUGF(4, ("cg6_mmap returning 0x%x\n", page));
|
|
|
|
return page;
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
STATIC
|
|
cg6_ioctl(dev, cmd, data, flag)
|
|
dev_t dev;
|
|
int cmd;
|
|
caddr_t data;
|
|
int flag;
|
|
{
|
|
register struct cg6_softc *softc = getsoftc(minor(dev));
|
|
int cursor_cmap;
|
|
int (*copyfun)();
|
|
static union {
|
|
u_char cmap[CG6_CMAP_ENTRIES];
|
|
u_long cur[32];
|
|
} iobuf;
|
|
|
|
DEBUGF(3, ("cg6_ioctl(%d, 0x%x)\n", minor(dev), cmd));
|
|
|
|
/* 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;
|
|
u_int entries;
|
|
|
|
cmap_ioctl:
|
|
cmap = (struct fbcmap *) data;
|
|
index = (u_int) cmap->index;
|
|
count = (u_int) cmap->count;
|
|
|
|
if (cursor_cmap == 0)
|
|
switch (PIX_ATTRGROUP(index)) {
|
|
case 0:
|
|
case PIXPG_8BIT_COLOR:
|
|
map = softc->cmap_rgb;
|
|
entries = CG6_CMAP_ENTRIES;
|
|
break;
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
else {
|
|
map = softc->omap_rgb;
|
|
entries = 2;
|
|
}
|
|
|
|
if ((index &= PIX_ALL_PLANES) >= entries ||
|
|
index + count > entries)
|
|
return EINVAL;
|
|
|
|
if (count == 0)
|
|
return 0;
|
|
|
|
if (cmd == FBIOPUTCMAP) {
|
|
register int error;
|
|
|
|
DEBUGF(3, ("FBIOPUTCMAP\n"));
|
|
if (cg6_update_pending(softc))
|
|
cg6_int_disable(softc);
|
|
|
|
map += index * 3;
|
|
|
|
/* sorry */
|
|
do {
|
|
if (error = COPY(copyfun, cmap->red,
|
|
iobuf.cmap, count))
|
|
break;
|
|
cg6_cmap_bcopy(iobuf.cmap, map++, count);
|
|
|
|
if (error = COPY(copyfun, cmap->green,
|
|
iobuf.cmap, count))
|
|
break;
|
|
cg6_cmap_bcopy(iobuf.cmap, map++, count);
|
|
|
|
if (error = COPY(copyfun, cmap->blue,
|
|
iobuf.cmap, count))
|
|
break;
|
|
cg6_cmap_bcopy(iobuf.cmap, map, count);
|
|
} while (_ZERO_);
|
|
|
|
if (error) {
|
|
if (cg6_update_pending(softc))
|
|
cg6_int_enable(softc);
|
|
return EFAULT;
|
|
}
|
|
|
|
/* cursor colormap update */
|
|
if (entries < CG6_CMAP_ENTRIES)
|
|
count = 0;
|
|
|
|
cg6_update_cmap(softc, index, count);
|
|
cg6_int_enable(softc);
|
|
}
|
|
else {
|
|
/* FBIOGETCMAP */
|
|
DEBUGF(3, ("FBIOGETCMAP\n"));
|
|
map += index * 3;
|
|
cg6_cmap_bcopy(iobuf.cmap, map++, -count);
|
|
if (COPYOUT(iobuf.cmap, cmap->red, count))
|
|
return EFAULT;
|
|
|
|
cg6_cmap_bcopy(iobuf.cmap, map++, -count);
|
|
if (COPYOUT(iobuf.cmap, cmap->green, count))
|
|
return EFAULT;
|
|
|
|
cg6_cmap_bcopy(iobuf.cmap, map, -count);
|
|
if (COPYOUT(iobuf.cmap, cmap->blue, count))
|
|
return EFAULT;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FBIOGATTR: {
|
|
register struct fbgattr *attr = (struct fbgattr *) data;
|
|
|
|
DEBUGF(3, ("FBIOGATTR\n"));
|
|
*attr = cg6_attr;
|
|
data = (caddr_t) &attr->fbtype;
|
|
}
|
|
/* fall through */
|
|
|
|
case FBIOGTYPE: {
|
|
register struct fbtype *fb = (struct fbtype *) data;
|
|
|
|
if (cmd == FBIOGTYPE) {
|
|
DEBUGF(3, ("FBIOGTYPE\n"));
|
|
*fb = cg6_attr.fbtype;
|
|
fb->fb_type = FBTYPE_SUN4COLOR;
|
|
}
|
|
fb->fb_width = softc->_w;
|
|
fb->fb_height = softc->_h;
|
|
/* XXX not quite like a cg4 */
|
|
fb->fb_size = softc->size;
|
|
}
|
|
break;
|
|
|
|
#if NWIN > 0
|
|
|
|
case FBIOGPIXRECT:
|
|
((struct fbpixrect *) data)->fbpr_pixrect = &softc->pr;
|
|
|
|
DEBUGF(3, ("FBIOGPIXRECT\n"));
|
|
/* initialize pixrect and private data */
|
|
softc->pr.pr_ops = &cg6_pr_ops;
|
|
/* pr_size set in attach */
|
|
softc->pr.pr_depth = 8;
|
|
softc->pr.pr_data = (caddr_t) &softc->prd;
|
|
|
|
/* md_linebytes, md_image set in attach */
|
|
/* md_offset already zero */
|
|
softc->prd.mpr.md_primary = minor(dev);
|
|
softc->prd.mpr.md_flags = MP_DISPLAY | MP_PLANEMASK;
|
|
softc->prd.planes = 255;
|
|
|
|
/* enable video */
|
|
cg6_set_video(softc, _ONE_);
|
|
|
|
break;
|
|
|
|
#endif NWIN > 0
|
|
|
|
case FBIOSVIDEO:
|
|
DEBUGF(3, ("FBIOSVIDEO\n"));
|
|
cg6_set_video(softc, * (int *) data & FBVIDEO_ON);
|
|
break;
|
|
|
|
case FBIOGVIDEO:
|
|
DEBUGF(3, ("FBIOGVIDEO\n"));
|
|
* (int *) data =
|
|
cg6_get_video(softc) ? FBVIDEO_ON : FBVIDEO_OFF;
|
|
break;
|
|
|
|
case GRABPAGEALLOC: /* Window Grabber */
|
|
return(segcg6_graballoc(data));
|
|
case GRABATTACH: /* attach to free memory */
|
|
return(segcg6_grabattach(data));
|
|
case GRABPAGEFREE: /* Window Grabber */
|
|
return(segcg6_grabfree(data));
|
|
case GRABLOCKINFO: /* Window Grabber */
|
|
return(segcg6_grabinfo(data));
|
|
|
|
/* informational ioctls */
|
|
|
|
case FBIOGXINFO:
|
|
*(struct cg6_info *)data = softc->cg6info;
|
|
return(0);
|
|
|
|
case FBIOMONINFO:
|
|
*(struct mon_info *)data = softc->moninfo;
|
|
return(0);
|
|
|
|
/* vertical retrace interrupt */
|
|
|
|
case FBIOVERTICAL:
|
|
softc->vrtflag |= CG6VRTIOCTL;
|
|
{ /* kludge alert. */
|
|
int i;
|
|
|
|
i = splbio();
|
|
cg6_int_enable(softc);
|
|
/* look, ma! a critical section! right here! */
|
|
(void) sleep((caddr_t)softc, PRIBIO);
|
|
(void) splx(i);
|
|
} /* kludge complete */
|
|
|
|
return(0);
|
|
|
|
case FBIOVRTOFFSET:
|
|
*(int *)data = VRT_OFFSET;
|
|
return(0);
|
|
|
|
/* 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 */
|
|
cg6_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 */
|
|
cg6_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;
|
|
}
|
|
/* just trying to find out colormap size */
|
|
else {
|
|
cp->cmap.index = 0;
|
|
cp->cmap.count = 2;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case FBIOSCURPOS:
|
|
softc->cur.pos = * (struct fbcurpos *) data;
|
|
cg6_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;
|
|
}
|
|
|
|
default:
|
|
return ENOTTY;
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
cg6_poll()
|
|
{
|
|
register int i, serviced = 0;
|
|
register struct cg6_softc *softc;
|
|
|
|
/*
|
|
* Look for any frame buffers that were expecting an interrupt.
|
|
*/
|
|
|
|
DEBUGF(7, ("cg6_poll\n"));
|
|
for (softc = cg6_softc, i = ncg6; --i >= 0; softc++)
|
|
if (cg6_int_pending(softc)) {
|
|
if (cg6_update_pending(softc) || softc->vrtflag)
|
|
cg6_intr(softc);
|
|
else
|
|
/* XXX catch stray interrupts? */
|
|
cg6_int_disable(softc);
|
|
serviced++;
|
|
}
|
|
|
|
return serviced;
|
|
}
|
|
|
|
static void
|
|
cg6_intr(softc)
|
|
register struct cg6_softc *softc;
|
|
{
|
|
struct cg6_cmap *cmap = CG6VA_TO_CMAP(softc->base);
|
|
LOOP_T count = softc->cmap_count;
|
|
u_long *in;
|
|
u_long *out;
|
|
u_long tmp;
|
|
void hat_unload();
|
|
|
|
DEBUGF(7, ("cg6_intr(%d)\n", softc - cg6_softc));
|
|
|
|
if (softc->vrtflag & CG6VRTCTR) {
|
|
if (softc->vrtmaps == 0) {
|
|
softc->vrtflag &= ~CG6VRTCTR;
|
|
} else
|
|
*softc->vrtpage += 1;
|
|
}
|
|
|
|
if (softc->vrtflag & CG6VRTIOCTL) {
|
|
softc->vrtflag &= ~CG6VRTIOCTL;
|
|
wakeup((caddr_t)softc);
|
|
}
|
|
|
|
if (cg6_update_pending(softc)) {
|
|
#ifdef MULTIPROCESSOR
|
|
/* blech, foo. Need to keep user processes from touching the
|
|
* colormap registers while we're touching them. Do this
|
|
* by invalidating all user mappings here. Only blow away
|
|
* the cmap page from the mapping.
|
|
*/
|
|
|
|
{
|
|
register struct segcg6_data *ptr ;
|
|
for(ptr=softc->mappings; ptr != NULL; ptr = ptr->next_map)
|
|
if( ptr->flag & SEG_CMAP ) {
|
|
DEBUGF(5, ("removing mapping for seg %x at %x\n",
|
|
ptr->seg,
|
|
ptr->seg->s_base + CG6_VADDR_CMAP - ptr->offset));
|
|
hat_unload(ptr->seg,
|
|
ptr->seg->s_base + CG6_VADDR_CMAP - ptr->offset,
|
|
CG6_CMAP_SZ) ;
|
|
}
|
|
}
|
|
#endif MULTIPROCESSOR
|
|
|
|
|
|
/* load cursor color map */
|
|
if (softc->omap_update) {
|
|
in = &softc->omap_image.omap_long[0];
|
|
out = (u_long *) &cmap->omap;
|
|
|
|
/* background color */
|
|
cmap->addr = 1 << 24;
|
|
tmp = in[0];
|
|
*out = tmp;
|
|
*out = tmp <<= 8;
|
|
*out = tmp <<= 8;
|
|
|
|
/* foreground color */
|
|
cmap->addr = 3 << 24;
|
|
*out = tmp <<= 8;
|
|
tmp = in[1];
|
|
*out = tmp;
|
|
*out = tmp <<= 8;
|
|
}
|
|
|
|
/* load main color map */
|
|
if (count) {
|
|
LOOP_T index = softc->cmap_index;
|
|
|
|
in = &softc->cmap_image.cmap_long[0];
|
|
out = (u_long *) &cmap->cmap;
|
|
|
|
/* count multiples of 4 RGB entries */
|
|
count = (count + (index & 3) + 3) >> 2;
|
|
|
|
/* round index to 4 entry boundary */
|
|
index &= ~3;
|
|
|
|
cmap->addr = index << 24;
|
|
PTR_INCR(u_long *, in, index * 3);
|
|
|
|
/* copy 4 bytes (4/3 RGB entries) per loop iteration */
|
|
count *= 3;
|
|
PR_LOOPV(count,
|
|
tmp = *in++;
|
|
*out = tmp;
|
|
*out = tmp <<= 8;
|
|
*out = tmp <<= 8;
|
|
*out = tmp <<= 8);
|
|
|
|
softc->cmap_count = 0;
|
|
}
|
|
|
|
softc->omap_update = 0;
|
|
}
|
|
|
|
cg6_int_disable(softc);
|
|
if (softc->vrtflag)
|
|
cg6_int_enable(softc);
|
|
}
|
|
|
|
/*
|
|
* Initialize a colormap: background = white, all others = black
|
|
*/
|
|
static void
|
|
cg6_reset_cmap(cmap, entries)
|
|
register u_char *cmap;
|
|
register u_int entries;
|
|
{
|
|
bzero((char *) cmap, entries * 3);
|
|
cmap[0] = 255;
|
|
cmap[1] = 255;
|
|
cmap[2] = 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
|
|
cg6_update_cmap(softc, index, count)
|
|
struct cg6_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
|
|
cg6_cmap_bcopy(bufp, rgb, count)
|
|
register u_char *bufp, *rgb;
|
|
u_int count;
|
|
{
|
|
register LOOP_T rcount = count;
|
|
|
|
if (--rcount >= 0)
|
|
PR_LOOPVP(rcount,
|
|
*rgb = *bufp++;
|
|
rgb += 3);
|
|
else {
|
|
rcount = -rcount - 2;
|
|
PR_LOOPVP(rcount,
|
|
*bufp++ = *rgb;
|
|
rgb += 3);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* enable/disable/update HW cursor
|
|
*/
|
|
static
|
|
cg6_setcurpos(softc)
|
|
struct cg6_softc *softc;
|
|
{
|
|
CG6VA_TO_THC(softc->base)->l_thc_cursor =
|
|
softc->cur.enable ?
|
|
((softc->cur.pos.x - softc->cur.hot.x) << 16) |
|
|
((softc->cur.pos.y - softc->cur.hot.y) & 0xffff) :
|
|
CG6_CURSOR_OFFPOS;
|
|
}
|
|
|
|
/*
|
|
* load HW cursor bitmaps
|
|
*/
|
|
static
|
|
cg6_setcurshape(softc)
|
|
struct cg6_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 *) &CG6VA_TO_THC(softc->base)->l_thc_cursora00;
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
hw[i] = (tmp = mask[i] & edge);
|
|
hw[i + 32] = tmp & image[i];
|
|
}
|
|
}
|
|
|
|
static
|
|
cg6_reset(addr)
|
|
addr_t addr;
|
|
{
|
|
/* disable HW cursor */
|
|
CG6VA_TO_THC(addr)->l_thc_cursor = CG6_CURSOR_OFFPOS;
|
|
|
|
/* reinitialize TEC */
|
|
{
|
|
struct tec *tec = CG6VA_TO_TEC(addr);
|
|
|
|
tec->l_tec_mv = 0;
|
|
tec->l_tec_clip = 0;
|
|
tec->l_tec_vdc = 0;
|
|
}
|
|
|
|
/* reinitialize FBC config register */
|
|
{
|
|
u_int *fhc = CG6VA_TO_FHC(addr), rev, conf;
|
|
|
|
rev = *fhc >> FHC_CONFIG_REV_SHIFT & FHC_CONFIG_REV_MASK;
|
|
if (rev <= 4) {
|
|
|
|
/* PROM knows how to deal with LSC and above */
|
|
/* rev == 0 : FBC 0 (not available to customers) */
|
|
/* rev == 1 : FBC 1 */
|
|
/* rev == 2 : FBC 2 */
|
|
/* rev == 3 : Toshiba (never built) */
|
|
/* rev == 4 : Standard Cell (not built yet) */
|
|
/* rev == 5 : LSC */
|
|
conf = *fhc & FHC_CONFIG_RES_MASK |
|
|
FHC_CONFIG_CPU_68020;
|
|
#if FBC_REV0
|
|
/* FBC0: test window = 0, disable fast rops */
|
|
if (rev == 0)
|
|
conf |= FHC_CONFIG_TEST |
|
|
FHC_CONFIG_FROP_DISABLE;
|
|
else
|
|
#endif FBC_REV0
|
|
/* test window = 1K x 1K */
|
|
conf |= FHC_CONFIG_TEST |
|
|
(10 + 1) << FHC_CONFIG_TESTX_SHIFT |
|
|
(10 + 1) << FHC_CONFIG_TESTY_SHIFT;
|
|
|
|
/* FBC[01]: disable destination cache */
|
|
if (rev <= 1)
|
|
conf |= FHC_CONFIG_DST_DISABLE;
|
|
|
|
*fhc = conf;
|
|
}
|
|
}
|
|
|
|
/* reprogram DAC to enable HW cursor use */
|
|
{
|
|
struct cg6_cmap *cmap = CG6VA_TO_CMAP(addr);
|
|
|
|
/* command register */
|
|
cmap->addr = 6 << 24;
|
|
|
|
/* turn on CR1:0, overlay enable */
|
|
cmap->ctrl = cmap->ctrl | (0x3 << 24);
|
|
}
|
|
}
|
|
|
|
#if NOHWINIT
|
|
cg6_init(addr)
|
|
addr_t addr;
|
|
{
|
|
/* Initialize DAC */
|
|
{
|
|
register struct cg6_cmap *cmap = CG6VA_TO_CMAP(addr);
|
|
register char *p;
|
|
|
|
static char dacval[] = {
|
|
4, 0xff,
|
|
5, 0,
|
|
6, 0x73,
|
|
7, 0,
|
|
0
|
|
};
|
|
|
|
/* initialize DAC */
|
|
for (p = dacval; *p; p += 2) {
|
|
cmap->addr = p[0] << 24;
|
|
cmap->ctrl = p[1] << 24;
|
|
}
|
|
}
|
|
|
|
/* Initialize THC */
|
|
{
|
|
register struct thc *thc = CG6VA_TO_THC(addr);
|
|
int vidon;
|
|
|
|
vidon = thc_get_video(thc);
|
|
thc->l_thc_hcmisc = THC_HCMISC_RESET | THC_HCMISC_INIT;
|
|
thc->l_thc_hcmisc = THC_HCMISC_INIT;
|
|
|
|
thc->l_thc_hchs = 0x010009;
|
|
thc->l_thc_hchsdvs = 0x570000;
|
|
thc->l_thc_hchd = 0x15005d;
|
|
thc->l_thc_hcvs = 0x010005;
|
|
thc->l_thc_hcvd = 0x2403a8;
|
|
thc->l_thc_hcr = 0x00016b;
|
|
|
|
thc->l_thc_hcmisc = THC_HCMISC_RESET | THC_HCMISC_INIT;
|
|
thc->l_thc_hcmisc = THC_HCMISC_INIT;
|
|
|
|
if (vidon)
|
|
thc_set_video(thc, _ONE_);
|
|
|
|
DEBUGF(1, ("TEC rev %d\n",
|
|
thc->l_thc_hcmisc >> THC_HCMISC_REV_SHIFT &
|
|
THC_HCMISC_REV_MASK));
|
|
}
|
|
|
|
/*
|
|
* Initialize FHC for 1152 X 900 screen
|
|
*/
|
|
{
|
|
register u_int *fhc = CG6VA_TO_FHC(addr), rev;
|
|
|
|
rev = *fhc >> FHC_CONFIG_REV_SHIFT & FHC_CONFIG_REV_MASK;
|
|
DEBUGF(1, ("FBC rev %d\n", rev));
|
|
|
|
/*
|
|
* FBC0: disable fast rops
|
|
* FBC[01]: disable destination cache
|
|
*/
|
|
*fhc = FHC_CONFIG_1152 |
|
|
FHC_CONFIG_CPU_68020 |
|
|
FHC_CONFIG_TEST |
|
|
#if FBC_REV0
|
|
rev == 0 ? FHC_CONFIG_FROP_DISABLE : 0 |
|
|
#endif FBC_REV0
|
|
rev <= 1 ? FHC_CONFIG_DST_DISABLE : 0;
|
|
}
|
|
}
|
|
#endif NOHWINIT
|
|
|
|
|
|
/* external routines */
|
|
extern caddr_t getpages();
|
|
|
|
/*
|
|
* from here on down, is the lego segment driver. this virtualizes the
|
|
* lego register file by associating a register save area with each
|
|
* mapping of the lego device (each lego segment). only one of these
|
|
* mappings is valid at any time; a page fault on one of the invalid
|
|
* mappings saves off the current lego 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.
|
|
*/
|
|
|
|
/*
|
|
* Per-context info:
|
|
* many registers in the tec and fbc do
|
|
* not need to be saved/restored.
|
|
*/
|
|
struct cg6_cntxt
|
|
{
|
|
struct cg6_cntxt *link ; /* must be first */
|
|
struct seg *seg_list ; /* segments in this context */
|
|
int pid ;
|
|
int flag ;
|
|
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;
|
|
};
|
|
|
|
static struct cg6_cntxt shared_context = {NULL,NULL,0,} ;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Window Grabber lock management
|
|
*/
|
|
|
|
#define LOCK_PAGESIZE (8*1024)
|
|
#define LOCK_OFFBASE VRT_OFFSET+PAGESIZE
|
|
|
|
#define NLOCKS 16
|
|
#define LOCKTIME 3 /* seconds */
|
|
|
|
/* 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 */
|
|
};
|
|
|
|
static caddr_t trashpage=NULL; /* for trashing unwanted writes */
|
|
|
|
static struct seglock lock_list[NLOCKS];
|
|
struct seglock *segcg6_findlock();
|
|
|
|
|
|
/*
|
|
* This routine is called through the cdevsw[] table to handle
|
|
* the creation of lego (cg6) segments.
|
|
*/
|
|
/*ARGSUSED*/
|
|
int
|
|
cgsixsegmap(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;
|
|
{
|
|
register struct cg6_softc *softc = getsoftc(minor(dev));
|
|
struct segcg6_data dev_a;
|
|
int segcg6_create();
|
|
register i;
|
|
caddr_t seglock = 0 ;
|
|
int segvrt = 0;
|
|
|
|
enum as_res as_unmap();
|
|
int as_map();
|
|
|
|
DEBUGF(3, ("segmap: d=%x, of=%x, adr=%x, l=%x, p=%x, maxp=%x, f=%x\n",
|
|
dev, offset, addr, len, prot, maxprot, flags));
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
dev_a.flag = 0 ;
|
|
dev_a.link = NULL;
|
|
dev_a.cntxtp = NULL;
|
|
dev_a.prot = prot ;
|
|
dev_a.maxprot = maxprot ;
|
|
|
|
/*
|
|
* check to see if this is a VRT page
|
|
*/
|
|
|
|
if (offset == VRT_OFFSET) {
|
|
|
|
if (len != PAGESIZE)
|
|
return(EINVAL);
|
|
|
|
segvrt++;
|
|
if (softc->vrtmaps++ == 0) {
|
|
if (softc->vrtpage == NULL)
|
|
softc->vrtpage = (int *)getpages(1, KMEM_NOSLEEP);
|
|
*softc->vrtpage = 0;
|
|
softc->vrtflag |= CG6VRTCTR;
|
|
cg6_int_enable(softc);
|
|
}
|
|
|
|
} else
|
|
|
|
/*
|
|
* Check to see if this is an allocated lock page.
|
|
*/
|
|
|
|
if ((seglock = (caddr_t)segcg6_findlock((off_t)offset)) !=
|
|
(caddr_t) NULL) {
|
|
if (len != LOCK_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 (cgsixmmap(dev, (off_t)offset + i, (int)maxprot) == -1)
|
|
return (ENXIO);
|
|
|
|
/* classify it */
|
|
if( offset+len > CG6_VADDR_FBC &&
|
|
offset < CG6_VADDR_FBC+CG6_TEC_POFF )
|
|
dev_a.flag |= SEG_FBC ;
|
|
if( offset+len > CG6_VADDR_TEC &&
|
|
offset < CG6_VADDR_FBC+CG6_FBCTEC_SZ )
|
|
dev_a.flag |= SEG_TEC ;
|
|
if( offset+len > CG6_VADDR_COLOR &&
|
|
offset < CG6_VADDR_COLOR+softc->fbmappable )
|
|
dev_a.flag |= SEG_FB ;
|
|
#ifdef MULTIPROCESSOR
|
|
if( offset+len > CG6_VADDR_CMAP &&
|
|
offset < CG6_VADDR_CMAP+CG6_CMAP_SZ )
|
|
dev_a.flag |= SEG_CMAP ;
|
|
#endif MULTIPROCESSOR
|
|
DEBUGF(3,("segment flags=%x\n", dev_a.flag));
|
|
}
|
|
|
|
/* LSC DFB BUG KLUDGE: DFB must always be mapped private
|
|
* on the buggy (chip rev. 5) LSC chip. This is done to ensure
|
|
* that nobody ever touches the framebuffer without the segment
|
|
* driver getting involved to make sure the chips are idle. This
|
|
* involves taking a page fault, invalidating all other process's
|
|
* mappings to the fb, (and performing a context switch?)
|
|
*
|
|
* Under pixrects, which maps the chips and the FB all at once,
|
|
* the entire mapping becomes a context. This won't hurt
|
|
* pixrects but entails unnecessary context switching. Under
|
|
* other libraries such as XGL, which maps the chips private and
|
|
* the FB shared, the FB becomes part of the context. Programs
|
|
* which only map the FB will also become contexts which also
|
|
* entails unnecessary context switching. Such is life.
|
|
*/
|
|
#ifdef COMMENT
|
|
if( softc->chiprev == 5 && (dev_a.flag & SEG_FB) )
|
|
#endif /* COMMENT */
|
|
flags = (flags & ~MAP_TYPE) | MAP_PRIVATE ;
|
|
|
|
/* decide if this segment is part of a context. */
|
|
|
|
if( dev_a.flag & (softc->chiprev == 5 ? (SEG_FBC|SEG_TEC|SEG_FB) :
|
|
(SEG_FBC|SEG_TEC)) ) {
|
|
dev_a.flag |= SEG_CTXINIT ;
|
|
}
|
|
|
|
if ((flags & MAP_FIXED) == 0) {
|
|
/*
|
|
* Pick an address w/o worrying about
|
|
* any vac alignment contraints.
|
|
*/
|
|
map_addr(addr, len, (off_t)0, 0);
|
|
if (*addr == NULL)
|
|
return (ENOMEM);
|
|
} else {
|
|
/*
|
|
* User specified address -
|
|
* Blow away any previous mappings.
|
|
*/
|
|
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;
|
|
|
|
/*
|
|
* 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 (segvrt) {
|
|
dev_a.flag = SEG_VRT;
|
|
} else
|
|
if (seglock) {
|
|
dev_a.flag = SEG_LOCK;
|
|
}
|
|
#ifdef COMMENT
|
|
else if ((flags & MAP_TYPE) == MAP_SHARED) { /* shared mapping */
|
|
dev_a.flag |= SEG_SHARED;
|
|
}
|
|
#endif /* COMMENT */
|
|
|
|
return(as_map((struct as *)as, *addr, len, segcg6_create, (caddr_t)&dev_a));
|
|
}
|
|
|
|
static int segcg6_dup(/* seg, newsegp */);
|
|
static int segcg6_unmap(/* seg, addr, len */);
|
|
static int segcg6_free(/* seg */);
|
|
static faultcode_t segcg6_fault(/* seg, addr, len, type, rw */);
|
|
static int segcg6_checkprot(/* seg, addr, size, prot */);
|
|
static int segcg6_badop();
|
|
static int segcg6_incore();
|
|
|
|
static struct seg_ops segcg6_ops = {
|
|
segcg6_dup, /* dup */
|
|
segcg6_unmap, /* unmap */
|
|
segcg6_free, /* free */
|
|
segcg6_fault, /* fault */
|
|
segcg6_badop, /* faulta */
|
|
segcg6_badop, /* unload */
|
|
segcg6_badop, /* setprot */
|
|
segcg6_checkprot,/* checkprot */
|
|
segcg6_badop, /* kluster */
|
|
(u_int (*)()) NULL, /* swapout */
|
|
segcg6_badop, /* sync */
|
|
segcg6_incore /* incore */
|
|
};
|
|
|
|
/*
|
|
* Window Grabber Page Management
|
|
*/
|
|
|
|
/*
|
|
* search the lock_list list for the specified offset
|
|
*
|
|
*/
|
|
|
|
/*ARGSUSED*/
|
|
static
|
|
struct seglock *
|
|
segcg6_findlock(off)
|
|
off_t off;
|
|
{
|
|
register int i;
|
|
|
|
if ((unsigned long)off < LOCK_OFFBASE)
|
|
return((struct seglock *)NULL);
|
|
for(i=0;i<NLOCKS;i++)
|
|
if (off == lock_list[i].offset)
|
|
return(lock_list+i);
|
|
return((struct seglock *)NULL);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
segcg6_grabattach(cookiep) /* IOCTL */
|
|
caddr_t cookiep;
|
|
{
|
|
|
|
return(EINVAL);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static int
|
|
segcg6_grabinfo(pp) /* IOCTL */
|
|
caddr_t pp;
|
|
{
|
|
*(int *)pp = 0;
|
|
return (0);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
segcg6_graballoc(pp) /* IOCTL */
|
|
caddr_t pp;
|
|
{
|
|
register int i;
|
|
register struct seglock *lp = lock_list;
|
|
|
|
if (trashpage == NULL) {
|
|
trashpage = getpages(1,KMEM_NOSLEEP); /* for trashing unwanted writes */
|
|
for(i=0;i<NLOCKS;i++) /* initialize offsets */
|
|
lock_list[i].offset = (off_t)(LOCK_OFFBASE+LOCK_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_NOSLEEP);
|
|
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
|
|
segcg6_grabfree(pp) /* IOCTL */
|
|
caddr_t pp;
|
|
{
|
|
register struct seglock *lp;
|
|
void segcg6_timeout();
|
|
|
|
if ((lp = segcg6_findlock((off_t)*(int *)pp)) == NULL)
|
|
return(EINVAL);
|
|
lp->cr.flag |= UNGRAB;
|
|
lp->allocf = 0;
|
|
if (lp->sleepf) { /* client will acquire the lock in this case */
|
|
untimeout((ifunc)segcg6_timeout,(caddr_t)lp);
|
|
wakeup((caddr_t)lp->page);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
#if CG6DEBUG
|
|
static
|
|
dump_segs(cur)
|
|
struct cg6_cntxt *cur ;
|
|
{
|
|
register struct seg *segp ;
|
|
register struct segcg6_data *ptr ;
|
|
register struct cg6_cntxt *ctx = &shared_context ;
|
|
|
|
for(; ctx; ctx=ctx->link)
|
|
{
|
|
printf(" ctx %x%s%s: link=%x, seg_list=%x, pid=%d, flag=%x\n",
|
|
ctx, ctx==&shared_context ? " (shared)" : "",
|
|
ctx==cur ? "(!)" : "", ctx->link, ctx->seg_list,
|
|
ctx->pid, ctx->flag) ;
|
|
for(segp = ctx->seg_list; segp; segp=ptr->link)
|
|
{
|
|
ptr = (struct segcg6_data *)segp->s_data ;
|
|
printf(" seg %x: sd=%x, link=%x, flag=%x, cntxtp=%x\n",
|
|
segp, ptr, ptr->link, ptr->flag, ptr->cntxtp) ;
|
|
printf(" base=%x, size=%x, ofs=%x\n",
|
|
segp->s_base, segp->s_size, ptr->offset) ;
|
|
}
|
|
}
|
|
}
|
|
#endif CG6DEBUG
|
|
|
|
/*
|
|
* Insert a segment into the segment list of a context:
|
|
*
|
|
* 1) find out which context this belongs in:
|
|
* search all contexts for a context that matches the current PID
|
|
* and for which no segments of this type (fbc/tec/fb) have yet
|
|
* been assigned. If no such context is found, create one.
|
|
* 2) insert into list
|
|
*/
|
|
static
|
|
ctx_seg_insert(seg)
|
|
struct seg *seg;
|
|
{
|
|
register struct segcg6_data *dp = (struct segcg6_data *) seg->s_data ;
|
|
register struct cg6_cntxt *ctx ;
|
|
|
|
if( dp->flag & SEG_SHARED )
|
|
ctx = &shared_context ;
|
|
else
|
|
{
|
|
for(ctx = shared_context.link; ctx != NULL; ctx=ctx->link)
|
|
if( ctx->pid == u.u_procp->p_pid &&
|
|
(ctx->flag & dp->flag & (SEG_FBC|SEG_TEC|SEG_FB)) == 0 )
|
|
break ; /* use this context */
|
|
|
|
if( ctx == NULL ) /* no match, create a new one */
|
|
{
|
|
ctx = (struct cg6_cntxt *)
|
|
new_kmem_zalloc( sizeof (struct cg6_cntxt),KMEM_NOSLEEP) ;
|
|
if( ctx == NULL )
|
|
panic("cgsix: out of memory in ctx_seg_insert");
|
|
/* TODO: MT-Safe */
|
|
ctx->link = shared_context.link ;
|
|
shared_context.link = ctx ;
|
|
/* END: MT-Safe */
|
|
ctx->pid = u.u_procp->p_pid ;
|
|
ctx->flag = 0 ;
|
|
}
|
|
}
|
|
dp->cntxtp = ctx ;
|
|
DEBUGF(3, ("ctx_seg_insert: seg=%x, dp=%x, ctx=%x\n", seg,dp,ctx));
|
|
|
|
|
|
/* now that we have the context, link this segment into it */
|
|
|
|
/* TODO: MT-Safe */
|
|
dp->link = ctx->seg_list ;
|
|
ctx->seg_list = seg ;
|
|
ctx->flag |= dp->flag ;
|
|
/* END: MT-Safe */
|
|
}
|
|
|
|
/*
|
|
* Create a device segment. the lego context is initialized by the create args.
|
|
*/
|
|
static
|
|
int
|
|
segcg6_create(seg, argsp)
|
|
struct seg *seg;
|
|
caddr_t argsp;
|
|
{
|
|
register struct segcg6_data *dp;
|
|
|
|
dp = (struct segcg6_data *)
|
|
new_kmem_zalloc(sizeof (struct segcg6_data),KMEM_NOSLEEP);
|
|
*dp = *((struct segcg6_data *)argsp);
|
|
|
|
dp->seg = seg ;
|
|
seg->s_ops = &segcg6_ops;
|
|
seg->s_data = (char *)dp;
|
|
|
|
/* if this segment is part of a context, link it into that context */
|
|
if( dp->flag & SEG_CTXINIT )
|
|
ctx_seg_insert(seg) ;
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
if( dp->flag & SEG_GLOBAL_MAP ) {
|
|
register struct cg6_softc *softc = getsoftc(minor(dp->dev));
|
|
DEBUGF(3,("adding dp %x to softc list %x\n", dp, softc->mappings));
|
|
dp->next_map = softc->mappings ;
|
|
softc->mappings = dp ;
|
|
}
|
|
#endif MULTIPROCESSOR
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Duplicate seg and return new segment in newsegp. copy original lego context.
|
|
*/
|
|
static
|
|
int
|
|
segcg6_dup(seg, newseg)
|
|
struct seg *seg, *newseg;
|
|
{
|
|
register struct segcg6_data *sdp = (struct segcg6_data *)seg->s_data;
|
|
struct segcg6_data dev_a;
|
|
|
|
dev_a = *sdp ;
|
|
dev_a.link = NULL ;
|
|
dev_a.cntxtp = NULL ;
|
|
|
|
return segcg6_create(newseg, (caddr_t) &dev_a);
|
|
}
|
|
|
|
static
|
|
int
|
|
segcg6_unmap(seg, addr, len)
|
|
register struct seg *seg;
|
|
register addr_t addr;
|
|
u_int len;
|
|
{
|
|
register struct segcg6_data *sdp = (struct segcg6_data *)seg->s_data;
|
|
register struct segcg6_data *ndp ;
|
|
register struct seg *nseg;
|
|
addr_t nbase;
|
|
u_int nsize;
|
|
void hat_unload();
|
|
void hat_newseg();
|
|
static segcg6_lockfree();
|
|
|
|
segcg6_lockfree(seg);
|
|
|
|
DEBUGF(3, ("segcg6_unmap: seg=%x, addr=%x, len=%x\n", seg, addr, len));
|
|
DEBUGF(3, (" seg %x: base=%x, size=%x\n",
|
|
seg, seg->s_base, seg->s_size));
|
|
DEBUGF(3, (" sdp %x: link=%x, flag=%x, cntxtp=%x, dev=%x, offset=%x\n",
|
|
sdp, sdp->link, sdp->flag, sdp->cntxtp, sdp->dev,
|
|
sdp->offset)) ;
|
|
|
|
/*
|
|
* 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) {
|
|
DEBUGF(3, (" entire segment\n"));
|
|
seg_free(seg);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Check for beginning of segment
|
|
*/
|
|
|
|
if (addr == seg->s_base) {
|
|
DEBUGF(3, (" beginning of segment\n"));
|
|
sdp->offset += len;
|
|
seg->s_base += len;
|
|
seg->s_size -= len;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Check for end of segment
|
|
*/
|
|
if (addr + len == seg->s_base + seg->s_size) {
|
|
DEBUGF(3, (" end of segment\n"));
|
|
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.
|
|
*/
|
|
DEBUGF(3, (" middle of segment\n"));
|
|
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("segcg6_unmap seg_alloc");
|
|
|
|
nseg->s_ops = seg->s_ops;
|
|
|
|
/* figure out what to do about the new context */
|
|
|
|
ndp = (struct segcg6_data *)
|
|
new_kmem_alloc(sizeof (struct segcg6_data),KMEM_NOSLEEP);
|
|
nseg->s_data = (caddr_t)ndp ;
|
|
|
|
*ndp = *sdp ;
|
|
ndp->link = NULL ;
|
|
ndp->dev = sdp->dev ;
|
|
ndp->offset += addr - seg->s_base ;
|
|
|
|
if( ndp->flag & SEG_CTXINIT )
|
|
{
|
|
/* this is part of the context, link it in after seg */
|
|
/* TODO: MT-Safe */
|
|
ndp->link = sdp->link ;
|
|
sdp->link = nseg ;
|
|
/* END: MT-Safe */
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
segcg6_free(seg)
|
|
struct seg *seg;
|
|
{
|
|
register struct segcg6_data *sdp = (struct segcg6_data *)seg->s_data;
|
|
register struct cg6_softc *softc = getsoftc(minor(sdp->dev));
|
|
|
|
DEBUGF(3, ("segcg6_free: seg %x: base=%x, size=%x\n",
|
|
seg, seg->s_base, seg->s_size));
|
|
|
|
if (sdp->flag & SEG_VRT) {
|
|
if (--softc->vrtmaps == 0)
|
|
softc->vrtflag &= ~CG6VRTCTR;
|
|
}
|
|
if (sdp->flag & SEG_LOCK)
|
|
segcg6_lockfree(seg);
|
|
|
|
/* if this segment was part of a context, remove it from the
|
|
* context's segment list. If the segment list becomes empty
|
|
* and the context is not the shared context, remove that
|
|
* context from the context list.
|
|
*/
|
|
if( sdp->flag & SEG_CTXINIT )
|
|
{
|
|
register struct segcg6_data *ptr ;
|
|
for( ptr = (struct segcg6_data *) &sdp->cntxtp->seg_list ;
|
|
ptr != NULL ;
|
|
ptr = (struct segcg6_data *) ptr->link->s_data )
|
|
if( ptr->link == seg ) {
|
|
ptr->link = sdp->link ;
|
|
break ;
|
|
}
|
|
/* did that empty the context? */
|
|
if( sdp->cntxtp != &shared_context && sdp->cntxtp->seg_list == NULL )
|
|
{
|
|
register struct cg6_cntxt *ctx ;
|
|
|
|
if( sdp->cntxtp == softc->curcntxt )
|
|
softc->curcntxt = NULL ;
|
|
for( ctx = (struct cg6_cntxt *)&shared_context.link;
|
|
ctx != NULL ;
|
|
ctx = ctx->link )
|
|
if( ctx->link == sdp->cntxtp ) {
|
|
ctx->link = sdp->cntxtp->link ;
|
|
break ;
|
|
}
|
|
kmem_free((char *)sdp->cntxtp, sizeof(*sdp->cntxtp)) ;
|
|
}
|
|
}
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
/* remove this segment from the device's global mapping list */
|
|
if( sdp->flag & SEG_GLOBAL_MAP ) {
|
|
DEBUGF(3,("removing sdp %x from list %x->%x\n", sdp, softc, softc->mappings));
|
|
if( softc->mappings == sdp )
|
|
softc->mappings = sdp->next_map ;
|
|
else {
|
|
register struct segcg6_data *ptr ;
|
|
for( ptr=softc->mappings;
|
|
ptr != NULL && ptr->next_map != sdp;
|
|
ptr = ptr->next_map ) ;
|
|
if( ptr != NULL )
|
|
ptr->next_map = sdp->next_map ;
|
|
}
|
|
}
|
|
#endif MULTIPROCESSOR
|
|
|
|
kmem_free((char *)sdp, sizeof(*sdp)) ;
|
|
DUMP_SEGS(3, sdp->cntxtp) ;
|
|
|
|
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
|
|
segcg6_lockfree(seg)
|
|
struct seg *seg;
|
|
{
|
|
register struct segcg6_data *sdp = (struct segcg6_data *)seg->s_data;
|
|
struct seglock *lp;
|
|
void segcg6_timeout();
|
|
|
|
if ((lp = segcg6_findlock((off_t)sdp->offset))==NULL)
|
|
return;
|
|
lp->cr.flag |= UNGRAB;
|
|
if (lp->sleepf) {
|
|
untimeout((ifunc)segcg6_timeout,(caddr_t)lp);
|
|
wakeup((caddr_t)lp->page);
|
|
}
|
|
}
|
|
|
|
static void
|
|
segcg6_timeout(lp)
|
|
struct seglock *lp;
|
|
{
|
|
struct segproc *np;
|
|
void hat_devload();
|
|
|
|
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,trashpage);
|
|
np->flag |= (UNLOCKMAP | TRASHPAGE);
|
|
}
|
|
wakeup((caddr_t)lp->page);
|
|
}
|
|
|
|
/*
|
|
* Handle lock segment faults here...
|
|
*
|
|
*/
|
|
|
|
static
|
|
int
|
|
segcg6_lockfault(seg,addr)
|
|
register struct seg *seg;
|
|
addr_t addr;
|
|
{
|
|
register struct seglock *lp;
|
|
register struct segcg6_data *current
|
|
= (struct segcg6_data *)seg->s_data;
|
|
struct segproc *sp;
|
|
void hat_devload();
|
|
void hat_unload();
|
|
extern int hz;
|
|
int s;
|
|
|
|
/* look up the segment in the lock_list */
|
|
|
|
lp = segcg6_findlock((off_t)current->offset);
|
|
if (lp == NULL)
|
|
return (FC_MAKE_ERR(EFAULT));
|
|
|
|
s = splsoftclock();
|
|
|
|
if (lp->cr.flag & UNGRAB) {
|
|
CG6MAP(seg,addr,trashpage);
|
|
(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,trashpage);
|
|
lp->owner->flag |= TRASHPAGE;
|
|
lp->owner->flag |= UNLOCKMAP;
|
|
if (lp->owner->flag & LOCKMAP) {
|
|
CG6UNMAP(lp->owner->locksegp);
|
|
lp->owner->flag &= ~LOCKMAP;
|
|
}
|
|
untimeout((ifunc)segcg6_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((ifunc)segcg6_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,trashpage);
|
|
(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
|
|
faultcode_t
|
|
segcg6_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 segcg6_data *current
|
|
= (struct segcg6_data *)seg->s_data;
|
|
int pf, s;
|
|
register struct cg6_softc *softc = getsoftc(minor(current->dev));
|
|
void hat_devload();
|
|
void hat_unload();
|
|
register struct fbc *fbc = CG6VA_TO_FBC(softc->base) ;
|
|
|
|
if (type != F_INVAL) {
|
|
return(FC_MAKE_ERR(EFAULT));
|
|
}
|
|
|
|
if (current->flag & SEG_VRT) {
|
|
CG6MAP(seg, addr, softc->vrtpage);
|
|
return(0);
|
|
}
|
|
|
|
if (current->flag & SEG_LOCK)
|
|
return(segcg6_lockfault(seg,addr));
|
|
|
|
s = splvm();
|
|
#ifdef MULTIPROCESSOR
|
|
xc_attention();
|
|
#endif MULTIPROCESSOR
|
|
if (current->cntxtp && softc->curcntxt != current->cntxtp) {
|
|
/*
|
|
* time to switch lego contexts.
|
|
*/
|
|
if (softc->curcntxt != NULL) {
|
|
register struct seg *segp ;
|
|
|
|
DEBUGF(5, ( "unloading context %x, pid=%d\n",
|
|
softc->curcntxt, softc->curcntxt->pid)) ;
|
|
|
|
segp = softc->curcntxt->seg_list ;
|
|
while( segp != NULL )
|
|
{
|
|
/*
|
|
* wipe out old valid mapping.
|
|
*/
|
|
|
|
hat_unload(segp, segp->s_base, segp->s_size);
|
|
segp = ((struct segcg6_data *)segp->s_data)->link ;
|
|
}
|
|
|
|
/*
|
|
* switch hardware contexts: if and only if
|
|
* there is a legitimate context to restore.
|
|
*/
|
|
|
|
if( softc->curcntxt->flag & (SEG_FBC|SEG_TEC) )
|
|
if( !cg6_cntxsave(fbc,
|
|
CG6VA_TO_TEC(softc->base), softc->curcntxt)) {
|
|
DEBUGF(1, ("cgsix: context save failed\n"));
|
|
#ifdef MULTIPROCESSOR
|
|
xc_dismissed();
|
|
#endif MULTIPROCESSOR
|
|
(void)splx(s);
|
|
return(FC_HWERR);
|
|
}
|
|
}
|
|
DEBUGF(5, ("loading context %x, pid=%d\n",
|
|
current->cntxtp, current->cntxtp->pid));
|
|
|
|
if( current->cntxtp->flag & (SEG_FBC|SEG_TEC) )
|
|
if (!cg6_cntxrestore( fbc,
|
|
CG6VA_TO_TEC(softc->base), current->cntxtp)) {
|
|
DEBUGF(1, ("cgsix: context load failed\n"));
|
|
#ifdef MULTIPROCESSOR
|
|
xc_dismissed();
|
|
#endif MULTIPROCESSOR
|
|
(void)splx(s);
|
|
return(FC_HWERR);
|
|
}
|
|
|
|
/*
|
|
* switch software "context"
|
|
*/
|
|
softc->curcntxt = current->cntxtp;
|
|
}
|
|
|
|
for (adr = addr; adr < addr + len; adr += PAGESIZE) {
|
|
pf = cgsixmmap(current->dev,
|
|
(off_t)current->offset+(adr-seg->s_base),
|
|
PROT_READ|PROT_WRITE);
|
|
if (pf == -1) {
|
|
#ifdef MULTIPROCESSOR
|
|
xc_dismissed();
|
|
#endif MULTIPROCESSOR
|
|
(void)splx(s);
|
|
return (FC_MAKE_ERR(EFAULT));
|
|
}
|
|
|
|
hat_devload(seg, adr, pf, current->prot|PROT_USER, 0 );
|
|
}
|
|
#ifdef MULTIPROCESSOR
|
|
xc_dismissed();
|
|
#endif MULTIPROCESSOR
|
|
(void)splx(s);
|
|
return(0);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static
|
|
int
|
|
segcg6_checkprot(seg, addr, len, prot)
|
|
struct seg *seg;
|
|
addr_t addr;
|
|
u_int len, prot;
|
|
{
|
|
register struct segcg6_data *sdp = (struct segcg6_data *)seg->s_data;
|
|
|
|
return (sdp->prot & prot) == prot ? 0 : -1 ;
|
|
}
|
|
|
|
/*
|
|
* segdev pages are always "in core".
|
|
*/
|
|
/*ARGSUSED*/
|
|
static
|
|
segcg6_incore(seg, addr, len, vec)
|
|
struct seg *seg;
|
|
addr_t addr;
|
|
register u_int len;
|
|
register char *vec;
|
|
{
|
|
u_int v = 0;
|
|
|
|
for (len = (len + PAGEOFFSET) & PAGEMASK; len; len -= PAGESIZE,
|
|
v += PAGESIZE)
|
|
*vec++ = 1;
|
|
return (v);
|
|
}
|
|
|
|
static
|
|
segcg6_badop()
|
|
{
|
|
/*
|
|
* 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
|
|
|
|
|
|
static
|
|
cg6_cntxsave(fbc, tec, saved)
|
|
struct fbc *fbc;
|
|
struct tec *tec;
|
|
struct cg6_cntxt *saved;
|
|
{
|
|
int dreg; /* counts through the data registers */
|
|
u_int *dp; /* points to a tec data register */
|
|
|
|
/*
|
|
* wait for fbc not busy; but not forever
|
|
* XXX - what *should* we do if the FBC never goes idle?
|
|
*/
|
|
|
|
CDELAY(!(fbc->l_fbc_status & L_FBC_BUSY), CG6_FBC_WAIT);
|
|
if (fbc->l_fbc_status & L_FBC_BUSY) {
|
|
printf("cgsix: cntxsave: fbc stayed busy\n");
|
|
return 0;
|
|
}
|
|
|
|
DEBUGF(5, ("saving registers for %d\n", saved->pid)) ;
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
static
|
|
cg6_cntxrestore(fbc, tec, saved)
|
|
struct fbc *fbc;
|
|
struct tec *tec;
|
|
struct cg6_cntxt *saved;
|
|
{
|
|
int dreg;
|
|
u_int *dp;
|
|
|
|
/*
|
|
* wait for fbc not busy; but not forever
|
|
* XXX - what *should* we do if the FBC never goes idle?
|
|
*/
|
|
|
|
CDELAY(!(fbc->l_fbc_status & L_FBC_BUSY), CG6_FBC_WAIT);
|
|
if (fbc->l_fbc_status & L_FBC_BUSY) {
|
|
printf("cgsix: cntxrestore: fbc stayed busy\n");
|
|
return 0;
|
|
}
|
|
|
|
DEBUGF(5, ("restoring registers for %d\n", saved->pid)) ;
|
|
|
|
/*
|
|
* 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);
|
|
}
|