Files
Arquivotheca.SunOS-4.1.4/sys/sbusdev/tcx.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

3048 lines
68 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifdef SYSV
#pragma ident "@(#)tcx.c 1.1 94/10/31 SMI"
#else
#if !defined(lint) && !defined(NOID)
static char sccsid[] = "@(#)tcx.c 1.1 94/10/31 SMI";
#endif
#endif
/*
* Copyright 1993 1994, Sun Microsystems, Inc.
*/
/*
* A24 24-bit framebuffer.
*/
/*
* A24 theory of operation:
*
* The A24 is driven by mapping framebuffer memory into process address
* space and writing to it directly. There are some control registers
* that are also accessed by mapping into process address space, but
* access to these registers are normally limited to the window system
* only.
*
* The A24 is a (mostly) stateless framebuffer. This means that
* most operations are atomic to the extent that if a process accessing
* the framebuffer is interrupted, and then resumes later after the
* framebuffer state has been altered, there are no bad effects.
*
* Different operations on the A24 are achieved by referencing different
* address spaces. There is a 24-bit address space where 32-bit writes
* reference individual 24-bit pixels. There is an 8-bit address space
* where 8-bit writes reference the red channel of individual pixels
* (and 32-bit writes reference four pixels at a time.)
*
* In addition, there are special address spaces: The "stipple" address
* space allows a 64-bit control word to be written containing a color (24
* or 8 bit) and a 32-bit pixel mask. This allows 32 pixels to be written
* in a single operation. The address of the 64-bit write determines the
* starting pixel for the operation. The "blit" address space is similar
* to the "stipple" address space, except that the 64-bit control word
* specifies a source pixel address and a pixel mask rather than a color
* and a pixel mask.
*
* In addition, there are "raw 32", "raw stipple" and "raw blit" address
* spaces. These are identical to the 24-bit, stipple and blit address
* spaces, except that they also access the 2 control planes which determine
* how underlying pixels are interpreted by the dacs (modes are: 8-bit
* indexed, 24-bit indexed, 24-bit gamma-corrected and 24-bit raw).
*
* Virtual addresses and lengths for mmap() are defined in tcxreg.h. Since
* the framebuffer size is not engraved in stone, applications should
* determine mmap() sizes by multiplying linebytes*height*depth. Pixel depth
* is as follows:
*
* dfb8 1
* dfb24 4
* stip 8
* blit 8
* rdfb32 4
* rstip 8
* rblit 8
*
* linebytes is returned via the FBIOGXINFO ioctl.
*
* Registers used by the driver:
*
* HCMISC chiprev, reset, video enable, vblank, vbank interrupt
* LINECOUNT current line counter
* CURSOR_ADDRESS cursor address register
*
*
*
*
* Note on softc locking: Bug 1142190 requires that the softc struct
* *not* be locked during copyin/copyout. This is because copyin/copyout
* could generate disk I/O to swap in the user address space. If
* a vertical retrace interrupt should occur during this I/O, the tcx
* interrupt routine would block if softc were locked. This could block
* the disk I/O, causing system deadlock.
*/
#ifndef SYSV
#include "win.h"
#endif /* SYSV */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/conf.h>
#include <sys/stat.h>
#ifdef SYSV
#include <sys/file.h>
#include <sys/kmem.h>
#include <sys/modctl.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sysmacros.h> /* for ctob(), used by cg8reg.h */
#else
#include <sundev/mbvar.h>
#include <sun/autoconf.h>
#include <sun/vddrv.h>
extern int nulldev() ;
#endif
#ifdef SYSV
#include <sys/fbio.h>
#include <sys/visual_io.h>
#include <sys/tcxreg.h>
#include <sys/cg8reg.h>
#include <sys/cg3var.h>
#else
#include <sun/fbio.h>
#include <sundev/tcxreg.h>
#include <sbusdev/memfb.h>
#include <sundev/cg8reg.h>
#include <pixrect/cg3var.h>
#endif SYSV
#ifndef SYSV
#include <pixrect/pixrect.h>
#include <pixrect/pr_impl_util.h>
#include <pixrect/pr_planegroups.h>
#include <pixrect/memvar.h>
#endif /* SYSV */
#ifndef PIX_ATTRGROUP /* TODO: where does this come from? */
#define PIX_ATTRGROUP(attr) ((unsigned)(attr) >> 25)
#define PIX_ALL_PLANES 0x00FFFFFF
#define PIXPG_8BIT_COLOR 2
#endif
#ifndef SYSV
/*
* This flag indicates that we should use kcopy instead of copyin
* for cursor ioctls.
*/
extern int fbio_kcopy;
extern int copyout(), copyin(), kcopy();
#endif /* !SYSV */
/* configuration options */
#ifndef TCXDEBUG
#define TCXDEBUG 0
#endif
#ifndef POWER_MGT /* TODO: change this to SYSV */
#define POWER_MGT
#else
#endif
#ifndef SYSV
#ifdef POWER_MGT
#undef POWER_MGT
#endif
#endif
#ifndef NWIN
#define NWIN 0
#endif
#if NOHWINIT
int tcx_hwinit = 1;
#endif NOHWINIT
#define TCXUNIT(dev) getminor(dev)
/*
* The Xterminal kernel is linked static (i.e. no
* loadable drivers. Therefore some driver entry
* points must be visable externally.
*/
#ifndef LOADABLE
#define STATIC
#else
#define STATIC static
#endif LOADABLE
/* stupid 4.x/5.x incompatibilities */
#ifdef SYSV
typedef _VOID *malloc_t ;
#else
typedef caddr_t malloc_t ;
typedef struct dev_info dev_info_t ;
#define volatile
#endif
/* Debugging-related stuff starts here */
#ifdef SYSV
static void
tcx_printf(char *format, ...)
{
va_list ap;
va_start(ap, format);
vcmn_err(CE_CONT, format, ap) ;
va_end(ap) ;
}
#else
#define tcx_printf printf
#endif
#if TCXDEBUG
static
tcx_assfail(line,ass)
int line ;
char *ass ;
{
tcx_printf("tcx: line %d: assertion failed \"%s\"\n", line,ass);
}
#ifdef SYSV
#define ASRT(e) do{if(!(e)) tcx_assfail(__LINE__,#e) ;}while(0)
#else
#define ASRT(e) do{if(!(e)) tcx_assfail(__LINE__,"e") ;}while(0)
#endif /* SYSV */
int tcx_debug = TCXDEBUG ;
int tcx_mutex_debug = 0 ;
int tcx_mem_debug = 0 ;
/* 0=no vrt retrace interrupts
* 1=only from ioctl
* 2=ioctl + vrtpage
* 3= + cmap sync
*/
/*
* 4= + mmap return values
* 5= + memory debug + mutex debug + colormap debug
* 6= + interrupt wait debug
* 7= + interrupt debug
*/
#define DEBUGF(level, args) \
do{if (tcx_debug >= (level)) tcx_printf args;}while(0)
#define MuDEBUGF(level, args) \
do{if (tcx_mutex_debug >= (level)) tcx_printf args;}while(0)
#define MDEBUGF(level, args) \
do{if (tcx_mem_debug >= (level)) tcx_printf args;}while(0)
/* memory-leak testing */
static int total_memory = 0 ;
static malloc_t
my_kmem_zalloc(size, flag, line)
u_long size, flag, line ;
{
total_memory += size ;
MDEBUGF(1, ("zalloc line %d: %d bytes, total=%d\n",
line, size, total_memory));
return kmem_zalloc(size,flag) ;
}
static malloc_t
my_kmem_alloc(size, flag, line)
u_long size, flag, line ;
{
total_memory += size ;
MDEBUGF(1, ("alloc line %d: %d bytes, total=%d\n",
line, size, total_memory));
return kmem_alloc(size,flag) ;
}
static
my_kmem_free(ptr, size, line)
malloc_t ptr ;
size_t size ;
int line ;
{
total_memory -= size ;
ASRT(total_memory >= 0) ;
MDEBUGF(1, ("kmem_free line %d: %d bytes, total=%d\n",
line, size, total_memory));
kmem_free(ptr, size) ;
}
#define tcx_kmem_zalloc(s,f) my_kmem_zalloc(s,f,__LINE__)
#define tcx_kmem_alloc(s,f) my_kmem_alloc(s,f,__LINE__)
#define tcx_kmem_free(p,s) my_kmem_free(p,s,__LINE__)
/* mutex testing */
#ifdef SYSV
static int last_enter;
static int last_exit;
debug_mutex_enter(mp, line)
kmutex_t *mp ;
int line ;
{
MuDEBUGF(1, ("entering mutex %x at %d\n", mp, line));
if (MUTEX_HELD(mp) && mutex_owned(mp))
cmn_err(CE_WARN,
"tcx: mutex %x already held at %d, last entered at %d\n",
mp, line, last_enter);
else
mutex_enter(mp);
last_enter = line;
}
debug_mutex_exit(mp, line)
kmutex_t *mp ;
int line ;
{
MuDEBUGF(1, ("exiting mutex %x at %d\n", mp, line));
if (mutex_owned(mp))
mutex_exit(mp);
else
cmn_err(CE_WARN,
"tcx: mutex %x not held at %d, last exited at %d\n",
mp, line, last_exit);
last_exit = line;
}
#define tcx_mutex_assert(s) ASRT(mutex_owned(&s->mutex))
#define tcx_mutex_nassert(s) ASRT(!mutex_owned(&s->mutex))
#define tcx_mutex_enter(mp) debug_mutex_enter(mp,__LINE__)
#define tcx_mutex_exit(mp) debug_mutex_exit(mp,__LINE__)
#endif /* SYSV */
#else /* !TCXDEBUG */
#define DEBUGF(level, args) /* nothing */
#define MDEBUGF(level, args) /* nothing */
#define MuDEBUGF(level, args) /* nothing */
#define ASRT(e) /* nothing */
#define tcx_kmem_zalloc(s,f) kmem_zalloc(s,f)
#define tcx_kmem_alloc(s,f) kmem_alloc(s,f)
#define tcx_kmem_free(p,s) kmem_free(p,s)
#ifdef SYSV
#define tcx_mutex_assert(s) /* nothing */
#define tcx_mutex_nassert(s) /* nothing */
#define tcx_mutex_enter mutex_enter
#define tcx_mutex_exit mutex_exit
#endif
#endif TCXDEBUG
/* tcx definitions */
static void *statep = NULL ; /* units */
/*
* Table of TCX virtual addresses, sorted roughly by frequency of use.
*/
static struct mapping {
int vaddr; /* virtual (mmap offset) address */
short flags ; /* flags */
#define SUSER 1 /* super-user only */
short idx ; /* index into mappings */
} tcx_map[] = {
{ TCX_VADDR_DFB8, 0, TCX_REG_DFB8},
{ TCX_VADDR_DFB24, 0, TCX_REG_DFB24},
{ TCX_VADDR_STIP, 0, TCX_REG_STIP},
{ TCX_VADDR_BLIT, 0, TCX_REG_BLIT},
{ TCX_VADDR_RDFB32, 0, TCX_REG_RDFB32},
{ TCX_VADDR_RSTIP, 0, TCX_REG_RSTIP},
{ TCX_VADDR_RBLIT, 0, TCX_REG_RBLIT},
{ TCX_VADDR_TEC, 0, TCX_REG_TEC},
{ TCX_VADDR_CMAP, SUSER, TCX_REG_CMAP},
{ TCX_VADDR_THC, 0, TCX_REG_THC},
{ TCX_VADDR_ROM, 0, TCX_REG_ROM},
{ TCX_VADDR_DHC, SUSER, TCX_REG_DHC},
{ TCX_VADDR_ALT, 0, TCX_REG_ALT},
};
#define N_TCX_MAP (sizeof(tcx_map)/sizeof(struct mapping))
#ifdef POWER_MGT
/* power management components */
#define PM_BOARD 0 /* board is 0 */
#define PM_MONITOR 1 /* monitor is 1 */
#endif POWER_MGT
/* video state: boolean OR of these bits */
#define VS_VIDEO 1 /* video enabled */
#define VS_SYNC 2 /* sync enabled */
typedef struct {
volatile caddr_t addr ; /* kernel virtual address */
u_long size ; /* object size in bytes */
off_t map_size ; /* how much is mapped into kernel */
int pfn ; /* hat_getkpfnum() value */
} MapInfo ;
/* per-unit data */
struct tcx_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 */
int _linebytes ;
void *_fb; /* frame buffer address */
#endif NWIN > 0
MapInfo maps[TCX_REG_MAX] ;
off_t last_vaddr ; /* cached mapping */
u_long last_size ;
int last_pfn ;
/* TODO: dummy overlay planes for cg4, cg8 emulation? */
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;
u_char cmap_buffer[4][TCX_CMAP_ENTRIES] ; /* frgb */
u_char omap_buffer[4][2] ; /* rgb */
u_int vrtflag; /* vrt interrupt flag */
#define TCXVRTIOCTL 0x1 /* FBIOVERTICAL in effect */
#define TCXVRTCTR 0x2 /* vertical retrace counter */
#define TCXCMAPUD 0x4 /* colormap update pending */
#define TCXOMAPUD 0x8 /* overlay colormap update pending */
#define TCX_CMAP_WAIT 0x10 /* waiting for colormap update */
#define TCXCURUPD 0x20 /* cursor shape update pending */
int *vrtpage; /* pointer to VRT page */
int emulation; /* emulation type, normally tcx */
int em_depth ; /* emulated depth, normally 24 */
int depth ; /* real depth, depends on A24/FSV */
int capabilities ; /* alignment, rop, etc. */
int owner ; /* pid of owner */
int revision ; /* chip revision */
struct mon_info moninfo; /* info about this monitor */
dev_info_t *dip ; /* back pointer */
int unit ;
#ifdef SYSV
kmutex_t mutex ;
kcondvar_t sleepcv ;
ddi_iblock_cookie_t iblock ;
#endif /* SYSV */
int initialized ; /* prevents interrupt race cond. */
int vidstate ;
};
/* default structure for FBIOGATTR ioctl */
static struct fbgattr tcx_attr = {
FBTYPE_TCX,
0, /* owner */
/* fbtype structure, subject to change according to emulation mode */
/* type,width,height,depth,cmap size, fb size */
{FBTYPE_TCX, 0,0,24, TCX_CMAP_ENTRIES,0},
/* fbsattr structure: flags, emu_type, dev_specific */
{0, FBTYPE_TCX, { 0,0,0,0,0,0,0,0 } },
/* available emulation types */
{FBTYPE_SUN3COLOR, FBTYPE_MEMCOLOR, -1,-1}
};
/* Colormap structure */
typedef volatile struct {
u_long addr ;
u_long cmap ;
u_long ctrl ;
u_long omap ;
u_long f[2] ;
u_long ctrl2 ;
} Tcx_Cmap ;
/*
* forward references
*/
#ifdef SYSV
static int tcx_identify(dev_info_t *);
static int tcx_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void**);
static int tcx_attach(dev_info_t *, ddi_attach_cmd_t);
static int tcx_detach(dev_info_t *, ddi_detach_cmd_t);
#ifdef POWER_MGT
static int tcx_power(dev_info_t *, int, int);
#endif POWER_MGT
static int tcx_reset(dev_info_t *, ddi_reset_cmd_t);
static int tcx_open(dev_t *, int, int, cred_t *);
static int tcx_close(dev_t, int, int, cred_t *);
static int tcx_ioctl(dev_t, int, int, int, cred_t *, int *);
static int tcx_mmap(dev_t dev, off_t off, int prot);
static u_int tcx_intr(caddr_t);
static void tcx_reset_reg(struct tcx_softc *);
static void tcx_reset_cmap(struct tcx_softc *);
static void tcx_update_cmaps(struct tcx_softc *);
static int tcx_kmap(struct tcx_softc *, int, u_int) ;
static int tcx_unmap_all(struct tcx_softc *) ;
static void set_control_plane(struct tcx_softc *,int) ;
static void read_colormap(struct tcx_softc *,int,int,int,u_char *,
u_char *,u_char *) ;
#else
static int tcx_identify();
static int tcx_getinfo();
static int tcx_attach();
static int tcx_reset();
STATIC int tcx_open();
STATIC int tcx_close();
STATIC int tcx_ioctl();
STATIC int tcx_mmap();
static int tcx_poll();
static u_int tcx_intr();
static void tcx_reset_reg();
static void tcx_reset_cmap();
static void tcx_update_cmaps();
static int tcx_kmap() ;
static int tcx_unmap_all() ;
static void set_control_plane() ;
static void read_colormap() ;
#endif
/*
* Tuning parameters
*/
#define MAX_VRT_LINE 10 /* if scanline count is less than this,
* we still have some useful vrt time
* remaining
*/
/*
* handy macros
*/
#ifdef SYSV
#define getsoftc(unit) ((struct tcx_softc *)ddi_get_soft_state(statep,(unit)))
#else
#define getsoftc(unit) (&tcx_softc[unit])
#endif
#define TEC(s,r) ((volatile u_long *)(s)->maps[TCX_REG_TEC].addr + (r))
#define THC(s,r) ((volatile u_long *)(s)->maps[TCX_REG_THC].addr + (r))
#define HCMISC(softc) (*THC(softc,TCX_THC_HCMISC))
#define LINECOUNT(softc) (*TEC(softc,TCX_TEC_LINECOUNT))
#define CURSOR_ADDRESS(softc) (*THC(softc,TCX_THC_CURSOR_ADDRESS))
#define CONFIG(softc) (*THC(softc,TCX_THC_CONFIG))
#define BZERO(d,c) bzero((caddr_t) (d), (size_t) (c))
#define BCOPY(s,d,c) bcopy((caddr_t) (s), (caddr_t) (d), (u_int) (c))
/* position value to use to disable HW cursor */
#define TCX_CURSOR_OFFPOS (0xffe0ffe0)
#define tcx_get_video(softc) ((softc)->vidstate & VS_VIDEO)
#ifdef TODO /* TODO: re-enable when rev0/1 chips obsolete */
#define tcx_int_enable(softc) (HCMISC(softc) |= TCX_HCMISC_EN_IRQ)
#endif /* TODO */
#define tcx_int_disable(softc) (HCMISC(softc) &= ~TCX_HCMISC_EN_IRQ)
#define tcx_int_pending(softc) (!(HCMISC(softc) & TCX_HCMISC_IRQ))
/* check if color map update is pending */
#define tcx_update_pending(softc) ((softc)->vrtflag & (TCXCMAPUD|TCXOMAPUD))
static void
tcx_set_vidstate(softc, state)
struct tcx_softc *softc ;
int state ;
{
register u_long hcmisc = HCMISC(softc) ;
register u_long bits ;
switch( state ) {
case 0:
case VS_VIDEO:
bits = TCX_HCMISC_DISABLE_VSYNC|TCX_HCMISC_DISABLE_HSYNC ;
break ;
case VS_SYNC:
bits = 0 ;
break ;
case VS_SYNC|VS_VIDEO:
bits = TCX_HCMISC_ENABLE_VIDEO ;
break ;
}
hcmisc &= ~(TCX_HCMISC_VSYNC_LEVEL|
TCX_HCMISC_HSYNC_LEVEL|
TCX_HCMISC_DISABLE_VSYNC|
TCX_HCMISC_DISABLE_HSYNC|
TCX_HCMISC_ENABLE_VIDEO) ;
hcmisc |= bits ;
HCMISC(softc) = hcmisc ;
softc->vidstate = state ;
}
static void
tcx_set_video(softc,f)
struct tcx_softc *softc ;
int f ;
{
tcx_set_vidstate(softc, f ? softc->vidstate|VS_VIDEO :
softc->vidstate&~VS_VIDEO) ;
}
static void
tcx_int_enable(softc)
struct tcx_softc *softc ;
{
volatile u_long *hcmisc = &HCMISC(softc) ;
if( softc->revision == 0 ) /* wait for end of vrt */
{
while( (*hcmisc & TCX_HCMISC_VBLANK) != 0 )
#ifdef SYSV
drv_usecwait(100) ;
#else
DELAY(100) ;
#endif
}
*hcmisc |= TCX_HCMISC_EN_IRQ ;
}
/* simulate SVr4-specific functions in 4.x */
#ifndef SYSV
typedef int cred_t ; /* dummy for tcx_close */
typedef int ddi_attach_cmd_t ; /* dummy for attach()*/
static int ntcx=0, next_unit=0 ;
static struct tcx_softc *tcx_softc = NULL ;
#define getpid() (u.u_procp->p_pid)
#define pagesize PAGESIZE
#define pageoffset PAGEOFFSET
#define pageshift PAGESHIFT
#define tcx_getpages getpages
#define tcx_freepages freepages
#define DDI_IDENTIFIED ++ntcx
#define DDI_NOT_IDENTIFIED 0
#define DDI_SUCCESS (0)
#define DDI_FAILURE (-1)
#define DDI_INTR_CLAIMED 1
#define DDI_INTR_UNCLAIMED 0
#define getminor minor
#define mutex_init(mp,name,type,cookie)
#define tcx_mutex_enter(mp)
#define tcx_mutex_exit(mp)
#define tcx_mutex_assert(s)
#define tcx_mutex_nassert(s)
#define cv_init(cp,name,type,ptr)
static int
ddi_map_regs(dip, rnumber, kaddrp, offset, len)
dev_info_t *dip ;
u_int rnumber ;
caddr_t *kaddrp ;
off_t offset ;
off_t len ;
{
struct dev_reg *reg = &dip->devi_reg[rnumber] ;
*kaddrp = map_regs(reg->reg_addr + offset, len, reg->reg_bustype);
return *kaddrp != NULL ? DDI_SUCCESS : DDI_FAILURE ;
}
#define ddi_unmap_regs(dip, rnumber, kaddrp, offset, len) \
unmap_regs(*kaddrp, len, dip->devi_reg[rnumber].reg_bustype)
int
ddi_soft_state_zalloc(statep, unit)
void *statep ;
int unit ;
{
if( tcx_softc == NULL )
tcx_softc = (struct tcx_softc *)
tcx_kmem_zalloc((u_int) sizeof (struct tcx_softc) * ntcx,1);
return tcx_softc != NULL ? DDI_SUCCESS : DDI_FAILURE ;
}
#define ddi_soft_state_free(s,u) 0
#define ddi_set_driver_private(dip,softc) (dip)->devi_data = (softc)
#define ddi_report_dev report_dev
#define tgetprop(dip, name, def) getprop((dip)->devi_nodeid, (name),(def))
static char *
tgetlongprop(dip, name, rlen)
dev_info_t *dip ;
char *name ;
int *rlen ;
{
*rlen = getproplen(dip->devi_nodeid, name) ;
#if TCXDEBUG
total_memory += *rlen ;
#endif TCXDEBUG
return getlongprop(dip->devi_nodeid, name) ;
}
static int
tgetboolprop(dip,name)
dev_info_t *dip ;
char *name ;
{
int len ;
return getproplen(dip->devi_nodeid, name) != -1 ;
}
#define FKIOCTL 1
int
ddi_copyin(src, dst, len, flag)
caddr_t src, dst ;
int len, flag ;
{
return flag ? kcopy(src,dst,len) : copyin(src,dst,len) ;
}
int
ddi_copyout(src, dst, len, flag)
caddr_t src, dst ;
int len, flag ;
{
return flag ? kcopy(src,dst,len) : copyout(src,dst,len) ;
}
/* copy ioctl arguments in/out. These are no-ops in 4.x since
* the kernel does this for us. */
#define ARG_COPYIN(dst) 0
#define ARG_COPYOUT(src) 0
#define IMM_COPYIN(dst,type) ((dst) = *(type *)arg)
#define IMM_COPYOUT(src,type) (*(type *)arg = (src))
/* 4.x interrupt control */
#define splbio4x splbio
#define splx4x splx
#define tcx_sleep(softc) sleep((caddr_t)softc, PRIBIO)
#define tcx_wakeup(softc) wakeup((caddr_t)softc)
#else /* SVr4 */
static u_long
getpid()
{
u_long rval ;
if( drv_getparm(PPID, &rval) == -1 )
return -1 ;
else
return rval ;
}
static int pagesize ;
static int pageoffset ;
static int pageshift ;
static caddr_t
tcx_getpages(npages)
int npages ;
{
register caddr_t p1;
p1 = kmem_zalloc(ptob(npages), KM_SLEEP);
ASRT(p1 != NULL && !((u_int) p1 & pageoffset));
return (p1);
}
static void
tcx_freepages(ptr, size)
void *ptr ;
int size ;
{
ASRT (ptr != NULL && !((u_int)ptr & pageoffset));
kmem_free(ptr, ptob(size));
}
static int
tgetprop(dip, name, def)
dev_info_t *dip ;
char *name ;
int def ;
{
return ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, name, def) ;
}
static char *
tgetlongprop(dip, name, rlen)
dev_info_t *dip ;
char *name ;
int *rlen ;
{
char *rval ;
if( ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
name, (caddr_t) &rval, rlen) != DDI_PROP_SUCCESS )
rval = NULL ;
#if TCXDEBUG
else
total_memory += *rlen ;
#endif TCXDEBUG
return rval ;
}
static int
tgetboolprop(dip,name)
dev_info_t *dip ;
char *name ;
{
int len ;
return ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
name, &len) == DDI_PROP_SUCCESS ;
}
/* copy ioctl arguments in/out. */
#define ARG_COPYIN(dst) DDI_COPYIN(arg, dst, sizeof(*dst), copyflag)
#define ARG_COPYOUT(src) DDI_COPYOUT(src, arg, sizeof(*src), copyflag)
#define IMM_COPYIN(dst,type) DDI_COPYIN(arg, &dst, sizeof(type), copyflag)
#define IMM_COPYOUT(src,type) DDI_COPYOUT(&src, arg, sizeof(type), copyflag)
/* 5.x interrupt control. spl* are no-ops because the mutex
* mechanism does this for us. */
#define splbio4x() 0
#define splx4x(i) 0
#define tcx_sleep(softc) cv_wait(&softc->sleepcv, &softc->mutex)
#define tcx_wakeup(softc) cv_broadcast(&softc->sleepcv) ;
#endif /* !SYSV */
#if NWIN > 0
/*
* SunWindows specific stuff
*/
static tcx_pr_putcolormap();
/* kernel pixrect ops vector */
static struct pixrectops tcx_pr_ops = {
mem_rop,
tcx_pr_putcolormap,
mem_putattributes
};
#endif NWIN > 0
/* Physically load colormaps into dacs, normally called from
* interrupt. Only load flagged values. Clear flags as we
* go. Must be called from within mutex.
*/
static void
tcx_update_cmaps(softc)
register struct tcx_softc *softc ;
{
register unsigned char *red, *green, *blue, *flags;
register Tcx_Cmap *cmap ;
register int index ;
register int oflag ;
DEBUGF(5, ("tcx_update_cmaps softc=%x\n", softc));
tcx_mutex_assert(softc) ;
cmap = (Tcx_Cmap *) softc->maps[TCX_REG_CMAP].addr ;
ASRT(cmap != NULL) ;
if( softc->vrtflag & TCXCMAPUD )
{
flags = softc->cmap_buffer[0] ;
red = softc->cmap_buffer[1] ;
green = softc->cmap_buffer[2] ;
blue = softc->cmap_buffer[3] ;
oflag = 0 ;
for(index=0; index<TCX_CMAP_ENTRIES; ++index)
{
if( *flags ) {
if( !oflag )
cmap->addr = index<<24 ;
cmap->cmap = *red<<24 ;
cmap->cmap = *green<<24 ;
cmap->cmap = *blue<<24 ;
}
oflag = *flags ;
*flags++ = 0 ;
++red ; ++green ; ++blue ;
}
cmap->addr = 0 ; /* A24 seems to require this */
}
if( softc->vrtflag & TCXOMAPUD )
{
/* now load overlay colormap */
cmap->addr = 2<<24 ;
cmap->omap = softc->omap_buffer[1][0]<<24 ;
cmap->omap = softc->omap_buffer[2][0]<<24 ;
cmap->omap = softc->omap_buffer[3][0]<<24 ;
cmap->addr = 3<<24 ;
cmap->omap = softc->omap_buffer[1][1]<<24 ;
cmap->omap = softc->omap_buffer[2][1]<<24 ;
cmap->omap = softc->omap_buffer[3][1]<<24 ;
cmap->addr = 0 ; /* A24 seems to require this */
}
softc->vrtflag &= ~(TCXCMAPUD|TCXOMAPUD) ;
}
#if NWIN > 0
static
tcx_pr_putcolormap(pr, index, count, red, green, blue)
Pixrect *pr;
int index, count;
unsigned char *red, *green, *blue;
{
register struct tcx_softc *softc = getsoftc(mpr_d(pr)->md_primary);
DEBUGF(5, ("tcx_pr_putcolormap unit=%d index=%d count=%d\n",
mpr_d(pr)->md_primary, index, count));
return put_colormap(softc, 0, index, count, red, green, blue, 1) ?
PIX_ERR : 0 ;
}
#endif NWIN > 0
static
#ifdef SYSV
tcx_identify(dip)
dev_info_t *dip ;
#else
tcx_identify(name)
char *name ;
#endif
{
#ifdef SYSV
char *name = ddi_get_name(dip) ;
#endif
DEBUGF(1, ("tcx_identify(%s)\n", name));
if( strcmp(name, "SUNW,tcx") == 0 )
return DDI_IDENTIFIED ;
else
return DDI_NOT_IDENTIFIED;
}
/* Utility: create a kernel mapping and return page-frame # */
static int
tcx_kmap(softc,which,size)
struct tcx_softc *softc ;
int which ;
u_int size ;
{
DEBUGF(3, ("tcx_kmap: which=%d, size=%x\n",which,size));
tcx_mutex_enter(&softc->mutex) ;
if(ddi_map_regs(softc->dip, which, &softc->maps[which].addr, 0,size))
{
softc->maps[which].map_size = 0 ;
softc->maps[which].pfn = -1 ;
}
else {
softc->maps[which].map_size = size ;
softc->maps[which].pfn = hat_getkpfnum(softc->maps[which].addr);
}
tcx_mutex_exit(&softc->mutex) ;
DEBUGF(4, (" which=%d, va=%x\n", which, softc->maps[which].addr));
return softc->maps[which].pfn ;
}
#ifdef POWER_MGT
static int
tcx_resume(dip)
dev_info_t *dip ;
{
register struct tcx_softc *softc;
if((softc = (struct tcx_softc *)ddi_get_driver_private(dip)) == NULL)
return DDI_FAILURE ;
DEBUGF(1, ("tcx_resume\n"));
/* restore hardware state from softc */
mutex_enter(&softc->mutex);
/* reload cursor */
if( softc->capabilities & TCX_CAP_HW_CURSOR ) {
tcx_setcurshape(softc);
tcx_setcurpos(softc);
}
/* reload colormap */
softc->vrtflag |= TCXCMAPUD|TCXOMAPUD ;
tcx_update_cmaps(softc) ;
/* reset video */
/* anthing else? */
mutex_exit(&softc->mutex);
return DDI_SUCCESS ;
}
static int
tcx_suspend(softc)
register struct tcx_softc *softc;
{
int i ;
DEBUGF(1, ("tcx_suspend\n"));
mutex_enter(&softc->mutex);
/* get colormap into shadow copy */
read_colormap(softc,0,0,256, softc->cmap_buffer[1],
softc->cmap_buffer[2],softc->cmap_buffer[3]) ;
for(i=0; i<256; ++i) softc->cmap_buffer[0][i] = 1 ;
read_colormap(softc,1,0,2, softc->omap_buffer[1],
softc->omap_buffer[2],softc->omap_buffer[3]) ;
for(i=0; i<2; ++i) softc->omap_buffer[0][i] = 1 ;
/* anything else? */
mutex_exit(&softc->mutex);
return DDI_SUCCESS ;
}
#endif POWER_MGT
static int
tcx_attach(dip, cmd)
dev_info_t *dip;
ddi_attach_cmd_t cmd; /* not used in 4.x */
{
register struct tcx_softc *softc;
register caddr_t reg;
int w, h ;
u_long size ; /* # of pixels in fb */
char *tmp ;
int tmplen ;
int unit;
extern dev_info_t *top_devinfo; /* in autoconf.c */
char name[16] ;
#ifdef POWER_MGT
u_long timestamp[2] ;
int power[2] ;
#endif POWER_MGT
DEBUGF(1, ("tcx_attach cmd=%d\n", cmd));
#ifdef SYSV
switch( cmd ) {
case DDI_ATTACH:
break ;
#ifdef POWER_MGT
case DDI_RESUME:
return tcx_resume(dip) ;
#endif POWER_MGT
default:
return DDI_FAILURE ;
}
unit = ddi_get_instance(dip) ;
#else
unit = next_unit++ ;
#endif SYSV
DEBUGF(1, ("tcx_attach unit=%d\n", unit));
if( ddi_soft_state_zalloc(statep, unit) != DDI_SUCCESS )
return DDI_FAILURE ;
/* KLUDGE: on sparc and sun3 architecture, zalloc() sets pointers
* to NULL, so we won't do it here. This wouldn't be true on
* some other architectures.
*/
softc = getsoftc(unit);
softc->initialized = 0 ;
softc->dip = dip;
softc->unit = unit ;
ddi_set_driver_private(dip, (caddr_t)softc) ;
#ifndef SYSV
dip->devi_unit = unit ;
#endif
/* intialize softc */
/* get stipple alignment, rop capabilities, etc. from prom */
softc->capabilities =
tgetprop(dip, "stipple-align", 5) |
tgetprop(dip, "blit-width", 5) << TCX_CAP_BLIT_WIDTH_SH |
tgetprop(dip, "blit-height", 0) << TCX_CAP_BLIT_HEIGHT_SH |
tgetprop(dip, "control-planes", 2) << TCX_CAP_C_PLANES_SH |
(tgetboolprop(dip, "stip-rop") ? TCX_CAP_STIP_ROP : 0) |
(tgetboolprop(dip, "blit-rop") ? TCX_CAP_BLIT_ROP : 0) |
(tgetboolprop(dip, "plane-mask") ? TCX_CAP_PLANE_MASK : 0) |
(tgetboolprop(dip, "hw-cursor") ? TCX_CAP_HW_CURSOR : 0) |
(!tgetboolprop(dip, "tcx-8-bit") ? TCX_CAP_24_BIT : 0) ;
/* compute values to be returned by FBIOGTYPE, FBIOGATTR and
* FBIOMONINFO. "size" fields are not really meaningful in
* a framebuffer that is mapped in multiple ways. Applications
* will really have to figure this out from the screen dimensions.
* Applications get linebytes with the FBIOGXINFO ioctl.
*/
softc->_w = w = tgetprop(dip, "width", 1152);
softc->_h = h = tgetprop(dip, "height", 900);
softc->_linebytes = tgetprop(dip, "linebytes", w);
/* TODO: get these from hardware somehow (prom?) */
size = softc->_linebytes * h ;
if( TCX_DFB8_SZ > size )
size = TCX_DFB8_SZ ;
softc->maps[TCX_REG_DFB8].size = size ;
softc->maps[TCX_REG_STIP].size = size * sizeof(u_long) * 2 ;
softc->maps[TCX_REG_BLIT].size = size * sizeof(u_long) * 2 ;
if( softc->capabilities & TCX_CAP_24_BIT ) {
softc->maps[TCX_REG_DFB24].size = size * sizeof(u_long) ;
softc->maps[TCX_REG_RDFB32].size = size * sizeof(u_long) ;
softc->maps[TCX_REG_RSTIP].size = size * sizeof(u_long) * 2 ;
softc->maps[TCX_REG_RBLIT].size = size * sizeof(u_long) * 2 ;
softc->depth = 24 ;
}
else
{
softc->maps[TCX_REG_DFB24].size =
softc->maps[TCX_REG_RDFB32].size =
softc->maps[TCX_REG_RSTIP].size =
softc->maps[TCX_REG_RBLIT].size = 0 ;
softc->depth = 8 ;
}
/* these are engraved in stone */
softc->maps[TCX_REG_TEC].size = TCX_TEC_SZ ;
softc->maps[TCX_REG_CMAP].size = TCX_CMAP_SZ ;
softc->maps[TCX_REG_THC].size = TCX_THC_SZ ;
softc->maps[TCX_REG_ROM].size = TCX_ROM_SZ ;
softc->maps[TCX_REG_DHC].size = TCX_DHC_SZ ;
softc->maps[TCX_REG_ALT].size = TCX_ALT_SZ ;
softc->owner = -1 ;
/*
* get monitor attributes
*/
softc->moninfo.mon_type = tgetprop(dip, "montype", 0);
softc->moninfo.pixfreq = tgetprop(dip, "pixfreq", 929405);
softc->moninfo.hfreq = tgetprop(dip, "hfreq", 61795);
softc->moninfo.vfreq = tgetprop(dip, "vfreq", 66);
softc->moninfo.hfporch = tgetprop(dip, "hfporch", 32);
softc->moninfo.vfporch = tgetprop(dip, "vfporch", 2);
softc->moninfo.hbporch = tgetprop(dip, "hbporch", 192);
softc->moninfo.vbporch = tgetprop(dip, "vbporch", 31);
softc->moninfo.hsync = tgetprop(dip, "hsync", 128);
softc->moninfo.vsync = tgetprop(dip, "vsync", 4);
/*
* Map in registers. Hold off on mapping framebuffers until
* needed by user process. If any register set fails to map
* in, the attach fails.
*/
if( tcx_kmap(softc, TCX_REG_THC, TCX_THC_SZ) == -1 ||
tcx_kmap(softc, TCX_REG_TEC, TCX_TEC_SZ) == -1 ||
tcx_kmap(softc, TCX_REG_CMAP, TCX_CMAP_SZ) == -1 )
{
tcx_unmap_all(softc) ; /* free whatever we had */
return DDI_FAILURE ;
}
softc->vidstate = (HCMISC(softc) & TCX_HCMISC_ENABLE_VIDEO) ?
VS_SYNC|VS_VIDEO : VS_SYNC ;
/* arbitrarily set the cached mapping to one we know to be valid */
softc->last_vaddr = TCX_ADDR_TEC ;
softc->last_size = TCX_TEC_SZ ;
softc->last_pfn = softc->maps[TCX_REG_TEC].pfn ;
/* If prom has us mapped in, can we use that mapping? If not,
* we need to map the first page here for SunPC to read
*/
/* only use address property if we are console fb */
if( (reg = (caddr_t) tgetprop(dip, "address", -1)) != (caddr_t)-1 )
{
softc->maps[TCX_REG_DFB8].addr = reg ;
softc->maps[TCX_REG_DFB8].map_size = size ;
softc->maps[TCX_REG_DFB8].pfn = hat_getkpfnum(reg);
DEBUGF(2, ("tcx mapped by PROM\n"));
}
else
{
/* TODO: only map in 1 page? */
if ( tcx_kmap( softc, TCX_REG_DFB8, size ) == -1 )
{
tcx_unmap_all( softc );
return DDI_FAILURE;
}
DEBUGF(2, ("tcx mapped by driver\n"));
}
#if NWIN > 0
softc->_fb = (MPR_T *)softc->maps[TCX_REG_DFB8].addr;
#endif
/*
* TCX devices are not supported under 4.x as
* accelerated frame buffers. Therefore we
* return that we are a cg3. This will allow
* 4.x apps to function unmodified. Note that
* an exception is made for the Xterminal's
* microkernel is based on 4.x yet the terminals
* X server does support TCX.
*/
#if defined(SYSV) || defined(HAMLET)
softc->emulation = FBTYPE_TCX ;
softc->em_depth = softc->depth ;
#else
softc->emulation = FBTYPE_SUN3COLOR ;
softc->em_depth = 8 ;
#endif SYSV || HAMLET
softc->revision = (CONFIG(softc) >> TCX_CONFIG_REV_SHIFT) & 0xf ;
if( (tmp = tgetlongprop(dip, "emulation", &tmplen)) != NULL ) {
if ( strcmp ( tmp, "cgthree+" ) == 0 ) {
softc->emulation = FBTYPE_SUN3COLOR;
softc->em_depth = 8 ;
}
else if ( strcmp ( tmp, "cgeight+" ) == 0 )
softc->emulation = FBTYPE_MEMCOLOR;
tcx_kmem_free(tmp,tmplen) ;
}
/* attach interrupt */
#ifdef SYSV
if(ddi_add_intr(dip, 0, &softc->iblock, NULL, tcx_intr, (caddr_t)softc)
!= DDI_SUCCESS )
{
tcx_unmap_all(softc) ;
return DDI_FAILURE ;
}
#else
addintr(dip->devi_intr[0].int_pri, tcx_poll, dip->devi_name, unit);
#endif SYSV
mutex_init(&softc->mutex, "tcx mutex", MUTEX_DRIVER, softc->iblock) ;
cv_init(&softc->sleepcv, "tcx sleep", CV_DRIVER, NULL) ;
#if NOHWINIT
if (tcx_hwinit)
tcx_init(softc);
#endif NOHWINIT
tcx_reset_reg(softc);
#ifdef SYSV
sprintf(name,"tcx%d",unit) ;
if( ddi_create_minor_node(dip, name, S_IFCHR,
unit, DDI_NT_DISPLAY, 0) != DDI_SUCCESS )
{
cv_destroy(&softc->sleepcv) ;
mutex_destroy(&softc->mutex) ;
ddi_remove_intr(dip, 0, softc->iblock) ;
tcx_unmap_all(softc) ;
return DDI_FAILURE ;
}
#endif SYSV
#ifdef POWER_MGT
/* set power-management properties. item 0 is the a24
* board itself, which currently can't be powered down.
* item 1 is the monitor, which is powered down by removing
* sync.
*
* The pm_timestamp property is a time value indicating when
* the last activity was. A time of 0 is a magic cookie that
* says this component is never eligible for shutdown. A
* small number (representing a very old time) indicates that
* the device may be shut down at any time.
*/
timestamp[0] = 0 ; /* flag board as never eligable */
drv_getparm(TIME, &timestamp[1]);
(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
"pm_timestamp", (caddr_t)timestamp, sizeof (timestamp));
/* the pm_norm_pwr property defines the "on" power levels.
* currently, we can only turn the monitor on and off, but
* it's possible that there will be a later product that
* can set the brightness leve, so we'll define "on"
* level for the monitor as 255
*/
power[PM_BOARD] = 1 ;
power[PM_MONITOR] = 255 ;
(void) ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
"pm_norm_pwr", (caddr_t)power, sizeof (power));
#endif POWER_MGT
ddi_report_dev(dip);
#ifdef SYSV
tcx_printf("?tcx%d: revision %d, screen %dx%d\n",
unit, softc->revision, w,h) ;
#else
printf("tcx%d: revision %d, screen %dx%d\n",
unit, softc->revision, w,h) ;
#endif SYSV
softc->initialized = 1 ;
return DDI_SUCCESS;
}
static
tcx_unmap_all(softc)
register struct tcx_softc *softc;
{
dev_info_t *dip = softc->dip ;
int i,j ;
/* unmap all registers */
for(i=0; i<N_TCX_MAP; ++i)
{
j = tcx_map[i].idx ;
if( softc->maps[j].addr != NULL ) {
DEBUGF(3,("tcx_unmap_all: which = %d, addr=%x, size=%x\n",
j, softc->maps[j].addr, softc->maps[j].map_size));
ddi_unmap_regs(dip, 0, &softc->maps[j].addr,
0, softc->maps[j].map_size) ;
softc->maps[j].addr = NULL ;
softc->maps[j].map_size = 0 ;
}
}
if( softc->vrtpage != NULL ) {
tcx_freepages(softc->vrtpage, 1) ;
softc->vrtpage = NULL ;
}
#ifdef POWER_MGT
if( ddi_prop_remove(DDI_DEV_T_NONE, dip, "pm_timestamp")
!= DDI_PROP_SUCCESS ||
ddi_prop_remove(DDI_DEV_T_NONE, dip, "pm_norm_pwr")
!= DDI_PROP_SUCCESS )
cmn_err(CE_WARN, "tcx_detach: Unable to remove property");
#endif POWER_MGT
(void) ddi_soft_state_free(statep, softc->unit);
}
#ifdef SYSV
static int
tcx_detach(dip, cmd)
dev_info_t *dip ;
ddi_detach_cmd_t cmd ;
{
register struct tcx_softc *softc ;
int unit ;
DEBUGF(1, ("tcx_detach cmd=%d\n", cmd));
if((softc = (struct tcx_softc *)ddi_get_driver_private(dip)) == NULL)
return DDI_FAILURE ;
DEBUGF(1, ("tcx_detach, softc=%x, mem used=%d\n",
softc, total_memory));
switch( cmd ) {
case DDI_DETACH:
break ;
#ifdef POWER_MGT
case DDI_SUSPEND:
return tcx_suspend(softc) ;
#endif POWER_MGT
default:
return DDI_FAILURE ;
}
tcx_reset_reg(softc);
ddi_remove_minor_node(dip,NULL) ;
cv_destroy(&softc->sleepcv) ;
mutex_destroy(&softc->mutex) ;
ddi_remove_intr(dip, 0, softc->iblock) ;
tcx_unmap_all(softc) ;
return DDI_SUCCESS ;
}
#ifdef POWER_MGT
static int
tcx_power(dip, component, level)
dev_info_t *dip ;
int component, level ;
{
register struct tcx_softc *softc ;
int rval ;
DEBUGF(1, ("tcx_power, component=%d, level=%d\n", component, level));
if( level < 0 )
return DDI_FAILURE ;
if((softc = (struct tcx_softc *)ddi_get_driver_private(dip)) == NULL)
return DDI_FAILURE ;
tcx_mutex_enter(&softc->mutex) ;
switch(component) {
case PM_MONITOR: /* monitor */
/* for now, the power level is meaningless other than as
* an on/off flag
*/
tcx_set_vidstate(softc, level > 0 ? softc->vidstate|VS_SYNC :
softc->vidstate&~VS_SYNC) ;
rval = DDI_SUCCESS ;
break ;
case PM_BOARD: /* A24 board can't be shut down, just return */
default:
rval = DDI_FAILURE ;
break ;
}
tcx_mutex_exit(&softc->mutex) ;
return rval ;
}
#endif POWER_MGT
tcx_getinfo(dip, cmd, arg, result)
dev_info_t *dip ;
ddi_info_cmd_t cmd ;
void *arg ;
void **result ;
{
dev_t dev ;
int unit, rval ;
struct tcx_softc *softc ;
dev = (dev_t) arg ;
unit = TCXUNIT(dev) ;
switch( cmd ) {
case DDI_INFO_DEVT2DEVINFO:
softc = getsoftc(unit) ;
if( softc == NULL )
return DDI_FAILURE ;
*result = (void *) softc->dip ;
break ;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *) unit ;
break ;
default:
return DDI_FAILURE ;
}
return DDI_SUCCESS ;
}
#endif SYSV
#ifdef SYSV
static int
tcx_open(devp, flag, otyp, credp)
dev_t *devp;
int flag;
int otyp ;
cred_t *credp ;
#else
STATIC int
tcx_open(dev, flag)
dev_t dev;
int flag;
#endif SYSV
{
#ifdef SYSV
dev_t dev = *devp ;
#endif SYSV
struct tcx_softc *softc = getsoftc(TCXUNIT(dev));
DEBUGF(2, ("tcx_open(%d), mem used=%d, pid=%d\n",
TCXUNIT(dev), total_memory, getpid()));
tcx_mutex_enter(&softc->mutex) ;
if( softc->owner == -1 )
softc->owner = getpid() ;
HCMISC(softc) |= TCX_HCMISC_OPEN_FLAG ;
tcx_mutex_exit(&softc->mutex) ;
return 0 ;
}
/*ARGSUSED*/
STATIC
tcx_close(dev, flag, otyp, credp)
dev_t dev;
int flag;
int otyp ; /* not used in 4.x */
cred_t *credp ; /* not used in 4.x */
{
struct tcx_softc *softc = getsoftc(TCXUNIT(dev));
DEBUGF(2, ("tcx_close(%d), mem used=%d, pid=%d\n",
TCXUNIT(dev), total_memory, getpid()));
tcx_mutex_enter(&softc->mutex) ;
softc->cur.enable = 0;
softc->owner = -1 ;
HCMISC(softc) &= ~TCX_HCMISC_OPEN_FLAG ;
tcx_reset_reg(softc);
softc->vrtflag &= ~TCXVRTCTR ;
if( softc->vrtpage != NULL ) {
tcx_freepages(softc->vrtpage, 1) ;
softc->vrtpage = NULL ;
}
tcx_mutex_exit(&softc->mutex) ;
set_control_plane(softc,0) ;
return 0;
}
#define GET_MAP(softc,which,size) \
do{ if( (softc)->maps[which].addr == NULL ) \
(void) tcx_kmap(softc,which,size) ; \
} while(0)
/* used for mapping framebuffers and other big mappings. Only the
* first page of the framebuffer is actually mapped into kernel
* space. This function works by adding the page offset to the
* cached page-frame number.
*
* Note: this function will fail on architectures that do not
* store the page in the low-order bits of the page-frame number.
* What we really need is a kernel function to map physical
* addresses to page-frame numbers rather than mapping kernel
* virtual addresses to page-frame numbers.
*/
/*ARGSUSED*/
STATIC int
tcx_mmap(dev, off, prot)
dev_t dev;
register off_t off;
int prot;
{
register struct tcx_softc *softc = getsoftc(TCXUNIT(dev));
register int diff, i,idx ;
register int rval = -1 ;
if( softc->emulation != FBTYPE_TCX )
switch( softc->emulation ) {
case FBTYPE_MEMCOLOR:
if( (diff = off-CG8_VADDR_FB) >= 0 &&
diff < softc->maps[TCX_REG_DFB24].size )
off += TCX_VADDR_DFB24 - CG8_VADDR_FB ;
else if( (diff = off-CG8_VADDR_DAC) >= 0 &&
diff < softc->maps[TCX_REG_CMAP].size )
off += TCX_VADDR_CMAP - CG8_VADDR_DAC ;
#ifdef SYSV
/* TODO: where is this defined in 4.x? */
else if( (diff = off-CG8_VADDR_PROM) >= 0 &&
diff < PROMSIZE )
off += TCX_VADDR_ROM - CG8_VADDR_PROM ;
#endif /* SYSV */
break ;
case FBTYPE_SUN3COLOR:
if( (diff = off-CG3_MMAP_OFFSET) >= 0 &&
diff < softc->maps[TCX_REG_DFB8].size )
off += TCX_VADDR_DFB8 - CG3_MMAP_OFFSET ;
break ;
}
/* Speedup: compare offset to cached values */
if( (diff = off-softc->last_vaddr) >= 0 &&
diff < softc->last_size )
{
DEBUGF(off ? 4 : 2, ("tcx_mmap(%d, 0x%x) (cached), pid=%d\n",
TCXUNIT(dev), (u_int) off, getpid()));
rval = softc->last_pfn + (diff>>pageshift) ;
}
else if((diff = off-TCX_VADDR_VRT) >= 0 && diff < pagesize )
{
DEBUGF(off ? 3 : 2, ("tcx_mmap(%d, 0x%x) (vrt), pid=%d\n",
TCXUNIT(dev), (u_int) off, getpid()));
if( softc->vrtpage == NULL )
{
softc->vrtpage = (int *) tcx_getpages(1) ;
if( softc->vrtpage != NULL )
{
softc->vrtflag |= TCXVRTCTR ;
*softc->vrtpage = 0 ;
tcx_int_enable(softc);
}
}
if( softc->vrtpage != NULL )
rval = hat_getkpfnum((caddr_t) softc->vrtpage) ;
}
/* we don't want a mutex here, since this function needs to
* be fast. On the other hand, we need to have a mutex if
* we're going to call ddi_map_regs. This is done in tcx_kmap()
*/
else /* normal mapping */
{
DEBUGF(off ? 3 : 1, ("tcx_mmap(%d, 0x%x), pid=%d\n",
TCXUNIT(dev), (u_int) off, getpid()));
for(i=0; i<N_TCX_MAP; ++i) {
idx = tcx_map[i].idx ;
if( (diff=off-tcx_map[i].vaddr) >= 0 &&
diff < softc->maps[idx].size )
{
/* TODO: only map one page? */
GET_MAP(softc,idx,softc->maps[idx].size);
if( (rval=softc->maps[idx].pfn) != -1 )
{
rval += diff>>pageshift ;
softc->last_vaddr = tcx_map[i].vaddr ;
softc->last_size = softc->maps[idx].size ;
softc->last_pfn = softc->maps[idx].pfn ;
}
break ;
}
}
}
DEBUGF(4, ("tcx_mmap returning 0x%x\n", rval));
return rval;
}
#define DDI_COPYOUT(s,d,l,f) ddi_copyout((caddr_t)(s),(caddr_t)(d),(l),(f))
#define DDI_COPYIN(s,d,l,f) ddi_copyin((caddr_t)(s),(caddr_t)(d),(l),(f))
/* Colormap utilities, used by ioctl */
/* verify that given index & count are valid */
static int
verify_cmap(index,count,entries)
int index, count, entries ;
{
switch( PIX_ATTRGROUP(index) ) {
case 0:
case PIXPG_8BIT_COLOR:
break ;
default:
return EINVAL ;
}
index &= PIX_ALL_PLANES ;
if( count < 0 || index + count > entries)
return EINVAL;
return 0 ;
}
/* wait for an interrupt; must be called from within a mutex */
static
wait_for_interrupt(softc,flag)
register struct tcx_softc *softc ;
int flag ;
{
int i;
DEBUGF(6,("wait_for_interrupt: flag=%x\n",flag));
tcx_mutex_assert(softc) ;
/* if vertical sync is disabled, there won't be a vertical
* interrupt, so we just return now.
*/
if( HCMISC(softc) & TCX_HCMISC_DISABLE_VSYNC )
return ;
i = splbio4x() ;
softc->vrtflag |= flag ;
tcx_int_enable(softc);
/* look, ma! a critical section! right here! */
tcx_sleep(softc) ;
splx4x(i) ;
}
/* read colormap from dacs to buffer */
static void
read_colormap(softc,cursor_cmap,index,count,red,green,blue)
register struct tcx_softc *softc ;
int cursor_cmap,index,count ;
u_char *red,*green,*blue ;
{
int i ;
int rval ;
register u_char *rm,*gm,*bm ;
register Tcx_Cmap *cmap = (Tcx_Cmap *) softc->maps[TCX_REG_CMAP].addr ;
ASRT(cmap != NULL) ;
if( !cursor_cmap ) {
cmap->addr = index<<24 ;
for(i=count; --i >= 0;) {
*red++ = cmap->cmap >> 24 ;
*green++ = cmap->cmap >> 24 ;
*blue++ = cmap->cmap >> 24 ;
}
}
else {
cmap->addr = 1<<24 ;
red[0] = cmap->omap >> 24 ;
green[0] = cmap->omap >> 24 ;
blue[0] = cmap->omap >> 24 ;
cmap->addr = 3<<24 ;
red[1] = cmap->omap >> 24 ;
green[1] = cmap->omap >> 24 ;
blue[1] = cmap->omap >> 24 ;
}
cmap->addr = 0 ; /* A24 seems to require this */
}
/* fetch colormap from dacs to buffer, then copy it out */
/* TODO: 3-color cursor? */
static int
fetch_colormap(softc,cursor_cmap,index,count,red,green,blue,copyflag)
register struct tcx_softc *softc ;
int cursor_cmap,index,count ;
u_char *red,*green,*blue ;
int copyflag ;
{
int rval ;
/* See notes on 1142190 at top of file */
u_char rtmp[TCX_CMAP_ENTRIES],
gtmp[TCX_CMAP_ENTRIES],
btmp[TCX_CMAP_ENTRIES] ;
tcx_mutex_nassert(softc) ;
if( count == 0 )
return 0 ;
if( (rval = verify_cmap(index,count,
cursor_cmap ? 2 : TCX_CMAP_ENTRIES)) )
return rval ;
index &= PIX_ALL_PLANES ;
tcx_mutex_enter(&softc->mutex) ;
/* if an update is pending, wait for it */
if( tcx_update_pending(softc) )
wait_for_interrupt(softc,TCX_CMAP_WAIT) ;
read_colormap(softc,cursor_cmap,index,count,rtmp,gtmp,btmp) ;
tcx_mutex_exit(&softc->mutex) ;
if( DDI_COPYOUT(rtmp+index, red, count, copyflag) ||
DDI_COPYOUT(gtmp+index, green, count, copyflag) ||
DDI_COPYOUT(btmp+index, blue, count, copyflag))
return EFAULT ;
return 0 ;
}
/* copy colormap to buffer, then queue an update */
/* TODO: 3-color cursor? */
/* This function is NOT called within a mutex */
static int
put_colormap(softc,cursor_cmap,index,count,red,green,blue,copyflag)
register struct tcx_softc *softc ;
int cursor_cmap,index,count ;
u_char *red,*green,*blue ;
int copyflag ;
{
int i ;
register u_char *rm,*gm,*bm ;
int rval ;
u_long hcmisc ;
/* See notes on 1142190 at top of file */
u_char rtmp[TCX_CMAP_ENTRIES],
gtmp[TCX_CMAP_ENTRIES],
btmp[TCX_CMAP_ENTRIES] ;
if( count == 0 )
return 0 ;
tcx_mutex_nassert(softc) ;
if( DDI_COPYIN(red, rtmp, count, copyflag) ||
DDI_COPYIN(green, gtmp, count, copyflag) ||
DDI_COPYIN(blue, btmp, count, copyflag))
return EFAULT ;
if( (rval = verify_cmap(index,count,
cursor_cmap ? 2 : TCX_CMAP_ENTRIES)) )
return rval ;
index &= PIX_ALL_PLANES ;
i = splbio4x() ; /* BEGIN CRITICAL SECTION */
tcx_mutex_enter(&softc->mutex) ;
if( !cursor_cmap ) {
rm = softc->cmap_buffer[1] + index ;
gm = softc->cmap_buffer[2] + index ;
bm = softc->cmap_buffer[3] + index ;
}
else {
rm = softc->omap_buffer[1] + index ;
gm = softc->omap_buffer[2] + index ;
bm = softc->omap_buffer[3] + index ;
}
BCOPY(rtmp,rm,count) ;
BCOPY(gtmp,gm,count) ;
BCOPY(btmp,bm,count) ;
/* set flags to indicate update required */
if( !cursor_cmap ) {
rm = softc->cmap_buffer[0] + index ;
while(--count >= 0)
*rm++ = 1 ;
}
softc->vrtflag |= cursor_cmap ? TCXOMAPUD : TCXCMAPUD ;
/* If we're in the middle of vblank, or vertical sync is turned
* off, we can load it now */
/* TODO: check these numbers */
hcmisc = HCMISC(softc) ;
if( hcmisc & TCX_HCMISC_DISABLE_VSYNC ||
hcmisc & TCX_HCMISC_VBLANK && LINECOUNT(softc) < MAX_VRT_LINE )
{
tcx_update_cmaps(softc) ; /* load it right now */
}
else {
/* enable interrupt so we can load the hardware colormap */
tcx_int_enable(softc);
}
(void) splx4x(i); /* END CRITICAL SECTION */
tcx_mutex_exit(&softc->mutex) ;
return 0 ;
}
/* set up control planes for emulation */
static void
set_control_plane(softc,value)
register struct tcx_softc *softc ;
int value ;
{
u_long *dfb32 ;
register int len ;
off_t size ;
if( (softc->capabilities & TCX_CAP_C_PLANES) )
return ;
/* temporarily map rdfb32 */
len = softc->_linebytes * softc->_h ;
size = len * sizeof(u_long) ;
if( ddi_map_regs(softc->dip, TCX_REG_RDFB32, (caddr_t *)&dfb32, 0,size)
!= DDI_SUCCESS )
{
tcx_printf("tcx: unable to map fb at line %d\n", __LINE__) ;
return ;
}
/* TODO: do this with stipple register instead? */
{
register u_long *ptr = dfb32 ;
while( --len >= 0 ) {
*ptr = (*ptr & 0xffffff) | value ;
++ptr ;
}
}
ddi_unmap_regs(softc->dip, TCX_REG_RDFB32, (caddr_t *)&dfb32, 0, size);
}
/* macro to create variable "name" of type "type" pointing to
* ioctl argument. Under 4.x, the data has already been copied,
* so we set "name" = "arg". Under 5.x, we reserve space to hold
* the data and set "name" to point to that space; ARG_COPY* will
* move the data as appropriate.
*
* IOCTL_DATA(type,name) defines name to be a pointer to type
* ARG_COPYIN(name) brings data into name
* ARG_COPYOUT(name) sends data back out
* IMM_COPYIN(dst,type) copy arg to dst
* IMM_COPYOUT(src,type) copy src to arg
*/
#ifdef SYSV
#define IOCTL_DATA(type,name) type temp ; register type *name = &temp
#define E_INVALID EINVAL
#else
#define IOCTL_DATA(type,name) register type *name = (type *)arg
#define E_INVALID ENOTTY
#endif /* SYSV */
/*ARGSUSED*/
STATIC
tcx_ioctl(dev, cmd, arg, mode, credp, result)
dev_t dev;
int cmd;
int arg;
int mode;
cred_t *credp ;
int *result ;
{
register struct tcx_softc *softc = getsoftc(TCXUNIT(dev));
int rval = 0 ;
int cursor_cmap;
int copyflag ;
static union {
u_char cmap[TCX_CMAP_ENTRIES];
u_long cur[32];
} iobuf;
DEBUGF(3, ("tcx_ioctl(%d, 0x%x), pid=%d\n",
TCXUNIT(dev), cmd, getpid()));
/* default to updating normal colormap */
cursor_cmap = 0;
/* figure out if kernel or user mode call */
#ifdef SYSV
copyflag = mode & FKIOCTL ;
#else
copyflag = fbio_kcopy ? FKIOCTL : 0;
#endif /* SYSV */
switch (cmd) {
case FBIOGATTR: {
IOCTL_DATA(struct fbgattr,attr) ;
DEBUGF(3, ("FBIOGATTR, emu_type=%d\n", softc->emulation));
*attr = tcx_attr;
attr->owner = softc->owner == getpid() ? 0 : softc->owner;
attr->sattr.emu_type = softc->emulation;
attr->sattr.dev_specific[0] = softc->capabilities ;
attr->sattr.dev_specific[1] = (int) softc->maps[TCX_REG_DFB8].addr ;
attr->fbtype.fb_type = softc->emulation;
attr->fbtype.fb_width = softc->_w;
attr->fbtype.fb_height = softc->_h;
attr->fbtype.fb_depth = softc->em_depth ;
/* TODO: emulation-dependent? */
attr->fbtype.fb_size = softc->maps[TCX_REG_DFB8].size;
if( ARG_COPYOUT(attr) )
rval = EFAULT ;
}
break ;
case FBIOGTYPE: {
IOCTL_DATA(struct fbtype,fb) ;
*fb = tcx_attr.fbtype;
fb->fb_type = softc->emulation;
fb->fb_width = softc->_w;
fb->fb_height = softc->_h;
fb->fb_depth = softc->em_depth ;
/* TODO: emulation-dependent? */
fb->fb_size = softc->maps[TCX_REG_DFB8].size;
if( ARG_COPYOUT(fb) )
rval = EFAULT ;
}
break;
#if NWIN > 0
case FBIOGPIXRECT: {
IOCTL_DATA(struct fbpixrect,fbp) ;
fbp->fbpr_pixrect = &softc->pr;
DEBUGF(3, ("FBIOGPIXRECT\n"));
/* initialize pixrect and private data */
softc->pr.pr_ops = &tcx_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 = TCXUNIT(dev);
softc->prd.mpr.md_flags = MP_DISPLAY | MP_PLANEMASK;
softc->prd.planes = 255;
/* enable video */
tcx_set_video(softc, 1);
}
break;
#endif /* NWIN > 0 */
case FBIOPUTCMAP: {
IOCTL_DATA(struct fbcmap,cmap) ;
if( ARG_COPYIN(cmap) ) {
rval = EFAULT; break ;
}
rval = put_colormap(softc, 0, cmap->index, cmap->count,
cmap->red, cmap->green, cmap->blue, copyflag) ;
}
break;
case FBIOGETCMAP: {
IOCTL_DATA(struct fbcmap,cmap) ;
if( ARG_COPYIN(cmap) ) {
rval = EFAULT; break ;
}
rval = fetch_colormap(softc, 0, cmap->index, cmap->count,
cmap->red, cmap->green, cmap->blue, copyflag) ;
}
break ;
case FBIOSATTR: {
IOCTL_DATA(struct fbsattr,attr) ;
DEBUGF(3, ("FBIOSATTR\n"));
if( ARG_COPYIN(attr) ) {
rval = EFAULT; break ;
}
tcx_mutex_enter(&softc->mutex) ;
switch( attr->emu_type ) {
case FBTYPE_SUN3COLOR:
softc->emulation = attr->emu_type ;
softc->em_depth = 8 ;
set_control_plane(softc,0) ;
break ;
case FBTYPE_MEMCOLOR:
softc->emulation = attr->emu_type ;
softc->em_depth = softc->depth ;
set_control_plane(softc,0x1000000) ;
break ;
case FBTYPE_TCX:
softc->emulation = attr->emu_type ;
softc->em_depth = 24 ;
break ;
default:
rval = EINVAL ;
}
tcx_mutex_exit(&softc->mutex) ;
}
break ;
case FBIOSVIDEO: {
IOCTL_DATA(int,flag) ;
if( ARG_COPYIN(flag) ) {
rval = EFAULT; break ;
}
DEBUGF(3, ("FBIOSVIDEO, flag=%x\n",*flag));
tcx_mutex_enter(&softc->mutex) ;
tcx_set_video(softc, (*flag & FBVIDEO_ON) ? 1 : 0) ;
tcx_mutex_exit(&softc->mutex) ;
}
break;
case FBIOGVIDEO: {
IOCTL_DATA(int,flag) ;
tcx_mutex_enter(&softc->mutex) ;
*flag = tcx_get_video(softc) ? FBVIDEO_ON : FBVIDEO_OFF;
tcx_mutex_exit(&softc->mutex) ;
DEBUGF(3, ("FBIOGVIDEO, video=%d\n", *flag));
if( ARG_COPYOUT(flag) )
rval = EFAULT;
}
break;
case FBIOVERTICAL:
tcx_mutex_enter(&softc->mutex) ;
wait_for_interrupt(softc, TCXVRTIOCTL) ;
tcx_mutex_exit(&softc->mutex) ;
break ;
/* HW cursor control */
case FBIOSCURSOR: {
IOCTL_DATA(struct fbcursor,cp) ;
u_long image[32], mask[32] ;
int set ;
int cbytes;
if( !(softc->capabilities & TCX_CAP_HW_CURSOR) ) {
rval = E_INVALID ;
break ;
}
if( ARG_COPYIN(cp) ) {
rval = EFAULT; break ;
}
set = cp->set;
if (set & FB_CUR_SETSHAPE) {
if ((u_int) cp->size.x > 32 ||
(u_int) cp->size.y > 32)
{
rval = EINVAL;
break ;
} else {
/* compute cursor bitmap bytes */
cbytes = cp->size.y *
sizeof softc->cur.image[0];
/* copy cursor image from user */
if( DDI_COPYIN(cp->image, image, cbytes, copyflag))
{ rval = EFAULT; break; }
if( DDI_COPYIN(cp->mask, mask, cbytes, copyflag))
{ rval = EFAULT; break; }
}
}
tcx_mutex_enter(&softc->mutex) ;
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;
if (set & FB_CUR_SETSHAPE) {
softc->cur.size = cp->size;
/* copy cursor image into softc */
BZERO(softc->cur.image, sizeof softc->cur.image);
BCOPY(image, softc->cur.image, cbytes);
BZERO(softc->cur.mask, sizeof softc->cur.mask);
BCOPY(mask, softc->cur.mask, cbytes);
/* load into hardware */
tcx_setcurshape(softc);
}
/* update hardware */
tcx_setcurpos(softc);
tcx_mutex_exit(&softc->mutex) ;
/* load colormap */
if( rval == 0 && (set & FB_CUR_SETCMAP) )
{
rval = put_colormap(softc, 1, cp->cmap.index,
cp->cmap.count, cp->cmap.red,
cp->cmap.green, cp->cmap.blue, copyflag) ;
}
break;
}
case FBIOGCURSOR: {
IOCTL_DATA(struct fbcursor,cp) ;
int cbytes,index;
if( !(softc->capabilities & TCX_CAP_HW_CURSOR) ) {
rval = E_INVALID ;
break ;
}
if( ARG_COPYIN(cp) ) {
rval = EFAULT; break ;
}
tcx_mutex_enter(&softc->mutex) ;
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 */
/* TODO: is this right? What is the format for cursor
image? */
cbytes = softc->cur.size.y * sizeof softc->cur.image[0];
tcx_mutex_exit(&softc->mutex) ;
if( cp->image &&
DDI_COPYOUT(softc->cur.image, cp->image, cbytes,copyflag) )
{
rval = EFAULT ;
break ;
}
if( cp->mask &&
DDI_COPYOUT(softc->cur.mask, cp->mask, cbytes,copyflag) )
{
rval = EFAULT ;
break ;
}
/* if color pointers are non-null copy colormap */
if( cp->cmap.red && cp->cmap.green && cp->cmap.blue ) {
if( (rval = fetch_colormap( softc,1,
cp->cmap.index,
cp->cmap.count,
cp->cmap.red,
cp->cmap.green,
cp->cmap.blue,
copyflag)) )
break ;
}
/* just trying to find out colormap size */
/* TODO: 3-color colormap? */
else {
cp->cmap.index = 0;
cp->cmap.count = 2;
}
if( ARG_COPYOUT(cp) ) {
rval = EFAULT; break ;
}
break;
}
case FBIOSCURPOS: {
IOCTL_DATA(struct fbcurpos,cp) ;
if( !(softc->capabilities & TCX_CAP_HW_CURSOR) ) {
rval = E_INVALID ;
break ;
}
if( ARG_COPYIN(cp) ) {
rval = EFAULT; break ;
}
tcx_mutex_enter(&softc->mutex) ;
softc->cur.pos = *cp ;
tcx_setcurpos(softc);
tcx_mutex_exit(&softc->mutex) ;
break;
}
case FBIOGCURPOS: {
IOCTL_DATA(struct fbcurpos,cp) ;
if( !(softc->capabilities & TCX_CAP_HW_CURSOR) ) {
rval = E_INVALID ;
break ;
}
tcx_mutex_enter(&softc->mutex) ;
*cp = softc->cur.pos ;
tcx_mutex_exit(&softc->mutex) ;
if( ARG_COPYOUT(cp) )
rval = EFAULT;
break;
}
case FBIOGCURMAX: {
static struct fbcurpos curmax = { 32, 32 };
if( !(softc->capabilities & TCX_CAP_HW_CURSOR) ) {
rval = E_INVALID ;
break ;
}
IMM_COPYOUT(curmax, struct fbcurpos) ;
break;
}
/* informational ioctls */
case FBIOGXINFO: {
IOCTL_DATA(struct cg6_info,info) ;
tcx_mutex_enter(&softc->mutex) ;
info->accessible_width = softc->_w ;
info->accessible_height = softc->_h ;
info->line_bytes = softc->_w ; /* TODO? */
info->hdb_capable = 0 ;
info->vmsize = softc->maps[TCX_REG_RDFB32].size ;
info->boardrev = 0 ;
info->slot = 0 ;
tcx_mutex_exit(&softc->mutex) ;
if( ARG_COPYOUT(info) )
rval = EFAULT ;
break;
}
case FBIOMONINFO: {
IOCTL_DATA(struct mon_info,info) ;
tcx_mutex_enter(&softc->mutex) ;
*info = softc->moninfo ;
tcx_mutex_exit(&softc->mutex) ;
if( ARG_COPYOUT(info) )
rval = EFAULT ;
break ;
}
/* vertical retrace interrupt */
case FBIOVRTOFFSET: {
static int vaddr_vrt = TCX_VADDR_VRT ;
IMM_COPYOUT(vaddr_vrt, int) ;
break ;
}
#ifdef VIS_GETIDENTIFIER
case VIS_GETIDENTIFIER:
{
static struct vis_identifier vis_id = {"SUNWtcx"} ;
IMM_COPYOUT(vis_id, struct vis_identifier) ;
break;
}
#endif
#if TCXDEBUG
#ifdef POWER_MGT
#ifdef SYSV
case 254:
#else
case _IOW(F, 254, int):
#endif
{
IOCTL_DATA(int,level) ;
if( ARG_COPYIN(level) ) {
rval = EFAULT; break ;
}
tcx_power(softc->dip, 1, *level) ;
break ;
}
#endif POWER_MGT
#ifdef SYSV
case 255:
#else
case _IOW(F, 255, int):
#endif
IMM_COPYIN(tcx_debug, int) ;
if( tcx_debug == -1 )
tcx_debug = TCXDEBUG ;
tcx_mem_debug = (tcx_debug >> 8) & 0xff ;
tcx_mutex_debug = (tcx_debug >> 16) & 0xff ;
tcx_debug &= 0xff ;
tcx_printf("tcx_debug is now %d.%d.%d\n",
tcx_debug,tcx_mem_debug,tcx_mutex_debug);
break ;
#endif
default:
rval = E_INVALID;
}
return rval;
}
#ifndef SYSV
static
tcx_poll()
{
register int i, serviced = 0;
register struct tcx_softc *softc;
/*
* Look for any frame buffers that were expecting an interrupt.
*/
DEBUGF(7, ("tcx_poll\n"));
for (softc = tcx_softc, i = ntcx; --i >= 0; softc++)
if (tcx_int_pending(softc)) {
if( softc->vrtflag)
tcx_intr((caddr_t) softc);
else
/* XXX catch stray interrupts? */
tcx_int_disable(softc);
serviced++;
}
return serviced;
}
#endif /* !SYSV */
static u_int
tcx_intr(arg)
caddr_t arg ;
{
register struct tcx_softc *softc = (struct tcx_softc *)arg ;
u_long *in;
u_long *out;
u_long tmp;
void hat_unload();
volatile u_long *hcmisc = &HCMISC(softc) ;
DEBUGF(7, ("tcx_intr(%d), hcmisc=%x, vrtflags=%x\n",
softc->unit, *hcmisc, softc->vrtflag));
if( !softc->initialized || !tcx_int_pending(softc) )
return DDI_INTR_UNCLAIMED ;
tcx_mutex_enter(&softc->mutex) ;
tcx_int_disable(softc);
DEBUGF(8, (" %d: hcmisc=%x, vrtflags=%x\n",
__LINE__, *hcmisc, softc->vrtflag));
if( softc->vrtflag & TCXCURUPD ) {
DEBUGF(8, (" %d: updating cursor, hcmisc=%x, vrtflags=%x\n",
__LINE__, *hcmisc, softc->vrtflag));
softc->vrtflag &= ~TCXCURUPD ;
tcx_setcurshape(softc);
tcx_setcurpos(softc);
}
if (softc->vrtflag & (TCXVRTIOCTL|TCX_CMAP_WAIT)) {
softc->vrtflag &= ~(TCXVRTIOCTL|TCX_CMAP_WAIT) ;
tcx_wakeup(softc) ;
}
if( tcx_update_pending(softc) )
tcx_update_cmaps(softc) ;
DEBUGF(8, (" %d: hcmisc=%x, vrtflags=%x\n",
__LINE__, *hcmisc, softc->vrtflag));
if (softc->vrtflag & TCXVRTCTR) {
register int *vrtpage = softc->vrtpage ;
if( vrtpage == NULL ) {
softc->vrtflag &= ~TCXVRTCTR;
} else
*vrtpage += 1;
}
if (softc->vrtflag)
tcx_int_enable(softc);
DEBUGF(8, (" %d: hcmisc=%x, vrtflags=%x\n",
__LINE__, *hcmisc, softc->vrtflag));
tcx_mutex_exit(&softc->mutex) ;
return DDI_INTR_CLAIMED ;
}
/*
* enable/disable/update HW cursor
*/
static
tcx_setcurpos(softc)
struct tcx_softc *softc;
{
CURSOR_ADDRESS(softc) = softc->cur.enable ?
((softc->cur.pos.x - softc->cur.hot.x) << 16) |
((softc->cur.pos.y - softc->cur.hot.y) & 0xffff) :
TCX_CURSOR_OFFPOS;
}
/*
* load HW cursor bitmaps
* Note: I've liberalized this somewhat; caller is now allowed to
* put bits in image data where there are none in mask, if that's what
* they want.
*/
static
tcx_setcurshape(softc)
struct tcx_softc *softc;
{
u_long tmp, edge = 0;
u_long *image, *mask ;
volatile u_long *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 = THC(softc,TCX_THC_CURSORA00) ;
for (i = 0; i < 32; i++) {
hw[i] = mask[i] & edge ;
hw[i + 32] = image[i] & edge ;
}
}
static void
tcx_reset_reg(softc)
struct tcx_softc *softc ;
{
if( softc->maps[TCX_REG_THC].addr == NULL ||
softc->maps[TCX_REG_CMAP].addr == NULL )
return ;
/* turn off interrupts */
tcx_int_disable(softc) ;
tcx_set_vidstate(softc, softc->vidstate|VS_SYNC) ;
/* disable HW cursor */
if( softc->capabilities & TCX_CAP_HW_CURSOR )
CURSOR_ADDRESS(softc) = TCX_CURSOR_OFFPOS;
/* reprogram DAC to enable HW cursor use */
{
Tcx_Cmap *cmap = (Tcx_Cmap *)softc->maps[TCX_REG_CMAP].addr ;
/* command register */
cmap->addr = 6 << 24;
/* turn on CR1:0, overlay enable */
cmap->ctrl = cmap->ctrl | (0x3 << 24);
cmap->addr = 0 ; /* A24 seems to require this */
}
}
#ifdef SYSV
static int
tcx_reset(dip, cmd)
dev_info_t *dip ;
ddi_reset_cmd_t cmd ;
{
int unit ;
struct tcx_softc *softc ;
if( cmd != DDI_RESET_FORCE )
return DDI_FAILURE ;
unit = ddi_get_instance(dip) ;
softc = getsoftc(unit) ;
tcx_mutex_enter(&softc->mutex) ;
tcx_reset_reg(softc) ;
tcx_mutex_exit(&softc->mutex) ;
return DDI_SUCCESS ;
}
#endif /* SYSV */
#if NOHWINIT
tcx_init(softc)
struct tcx_softc *softc ;
{
/* Initialize DAC, TODO: use ctrl2? */
{
register Tcx_Cmap *cmap ;
register char *p;
/* initialize DAC */
GET_MAP(softc, TCX_REG_DHC, TCX_DHC_SZ);
cmap = (Tcx_Cmap *)softc->maps[TCX_REG_DHC].addr;
cmap->addr = 4<<24 ; /* read mask = 1's */
cmap->ctrl2 = 0xffffffff ;
cmap->addr = 5<<24 ; /* blink maks = 0's */
cmap->ctrl2 = 0 ;
cmap->addr = 6<<24 ; /* cr0 = overlay enabled */
cmap->ctrl2 = 0x73<<24 ;
cmap->addr = 7<<24 ; /* test0 = 0 */
cmap->ctrl2 = 0 ;
cmap->addr = 8<<24 ; /* cr1 = 0 unused */
cmap->ctrl2 = 0 ;
cmap->addr = 9<<24 ; /* cr2 = 0 */
cmap->ctrl2 = 0 ;
cmap->addr = 11<<24 ; /* test1 = 0 */
cmap->ctrl2 = 0 ;
if( softc->capabilities & TCX_CAP_24_BIT ) {
cmap->addr = 16<<24 ; /* cr3 = enable true color */
cmap->ctrl2 = 0xa0<<24 ;
cmap->addr = 17<<24 ; /* cr4 = 8/8/8 format, 64 pins*/
cmap->ctrl2 = 0xb2<<24 ;
}
cmap->addr = 0 ; /* overlay colors */
cmap->omap = 0xff0000ff ;
cmap->omap = 0xff0000ff ;
cmap->omap = 0xff0000ff ;
cmap->addr = 1<<24 ;
cmap->omap = 0xff0000ff ;
cmap->omap = 0xff0000ff ;
cmap->omap = 0xff0000ff ;
cmap->addr = 2<<24 ;
cmap->omap = 0xff0000ff ;
cmap->omap = 0xff0000ff ;
cmap->omap = 0xff0000ff ;
cmap->addr = 3<<24 ;
cmap->omap = 0 ;
cmap->omap = 0 ;
cmap->omap = 0 ;
cmap->addr = 0 ; /* A24 seems to require this */
}
/* Initialize THC */
{
volatile u_long *thc = (u_long *)softc->maps[TCX_REG_THC].addr ;
volatile u_long *hcmisc = thc + TCX_THC_HCMISC ;
volatile u_long *strap = thc + TCX_THC_STRAPPING ;
volatile u_long *delay = thc + TCX_THC_DELAY ;
int vidon;
*delay = 0x00011101 ; /* set video and cas delays=1 (2ns) */
/* Assume 1152x900@66 */
/* 1. Reload STRAP */
*strap = 0x004101d4 ; /* 0x004101d4 for 30MHz rev0 ASIC */
/* 2. Reset Video Timing Generator */
vidon = tcx_get_video(softc) ;
*hcmisc = 0x0000000f ; /* disable sync */
*hcmisc = 0x0000100f ; /* reset video (?) */
*hcmisc = 0x0000000f ; /* then write 0 */
/* load THC Timing register */
thc[TCX_TEC_HSS] = 9 ;
thc[TCX_TEC_HSE] = 0x29 ;
thc[TCX_TEC_HDS] = 0x5d ;
thc[TCX_TEC_HSEDVS] = 0x167 ;
thc[TCX_TEC_HDE] = 0x17d ;
thc[TCX_TEC_VSS] = 1 ;
thc[TCX_TEC_VSE] = 5 ;
thc[TCX_TEC_VDS] = 0x24 ;
thc[TCX_TEC_VDE] = 0x3a8 ;
/* 6. start up video timing */
#ifdef COMMENT
if (vidon)
#endif /* COMMENT */
*hcmisc = 0x00002486 ;
#ifdef COMMENT
else
*hcmisc = 0x00002086 ;
#endif /* COMMENT */
thc[TCX_THC_CURSOR_ADDRESS] = 0xffe0ffe0 ; /* -32,-32 */
if( softc->capabilities & TCX_CAP_24_BIT )
{
volatile u_long *alt ;
int i ;
GET_MAP(softc, TCX_REG_ALT, TCX_ALT_SZ);
alt = (u_long *)softc->maps[TCX_REG_ALT].addr ;
set_alt(alt, 0, 2) ;
set_alt(alt, 1, 0) ;
set_alt(alt, 2, 0) ;
set_alt(alt, 3, 2) ;
set_alt(alt, 4, 8) ;
set_alt(alt, 5, 1) ;
set_alt(alt, 6, 0) ;
set_alt(alt, 7, 0) ;
set_alt(alt, 8, 5) ;
set_alt(alt, 9, 0xa) ;
set_alt(alt, 10, 0) ;
set_alt(alt, 11, 1) ;
set_alt(alt, 12, 0) ;
set_alt(alt, 13, 0) ;
set_alt(alt, 14, 0) ;
set_alt(alt, 15, 0) ;
for (i=0; i< 32; i++ ) set_alt(alt,13,0);
}
#ifdef TODO
DEBUGF(1, ("TEC rev %d\n",
(*hcmisc & TCX_HCMISC_CHIPREV) >>
TCX_HCMISC_CHIPREV_SHIFT));
#endif /* TODO */
}
}
set_alt(addr,which,value)
u_long *addr ;
int which ;
int value ;
{
addr += which ;
value |= value<<4 ;
value |= value<<8 ;
value |= value<<16 ;
*addr = value ;
}
#endif NOHWINIT
/* 5.x loadable driver starts here */
#ifdef SYSV
static struct cb_ops tcx_cb_ops = {
tcx_open, /* open */
tcx_close, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nulldev, /* read */
nulldev, /* write */
tcx_ioctl, /* ioctl */
nodev, /* devmap */
tcx_mmap, /* mmap */
ddi_segmap, /* segmap */
nochpoll, /* chpoll */
ddi_prop_op, /* prop_op */
NULL, /* streams info */
D_NEW|D_MP, /* flags, see <sys/conf.h> */
} ;
static struct dev_ops tcx_ops = {
DEVO_REV, /* revision */
0, /* ref count */
tcx_getinfo, /* info */
tcx_identify, /* identify */
nulldev, /* probe */
tcx_attach, /* attach */
tcx_detach, /* detach */
tcx_reset, /* reset */
&tcx_cb_ops, /* driver operations */
(struct bus_ops *)0, /* bus operations */
#ifdef POWER_MGT
tcx_power,
#endif POWER_MGT
};
extern struct mod_ops mod_driverops ;
static struct modldrv modldrv = {
&mod_driverops,
"tcx driver 1.1",
&tcx_ops,
} ;
static struct modlinkage modlinkage = {
MODREV_1,
&modldrv,
0,
} ;
int
_init()
{
register int e ;
int i ;
if( (e=ddi_soft_state_init(&statep, sizeof(struct tcx_softc), 1)) != 0 )
return e ;
if( (e=mod_install(&modlinkage)) != 0 )
{
ddi_soft_state_fini(&statep) ;
return e ;
}
/* TODO: other module initialization as needed */
i = pagesize = ptob(1) ;
pageoffset = pagesize-1 ;
for(pageshift=0; (i>>=1) != 0; ++pageshift);
return e ;
}
int
_fini()
{
register int e ;
#ifdef COMMENT
if( /* some reason to veto unload */ )
return EBUSY ;
#endif /* COMMENT */
if( (e=mod_remove(&modlinkage)) != 0 )
return e ;
/* TODO: any driver-specific shutdown. free resources, etc. */
ddi_soft_state_fini(&statep) ;
ASRT(total_memory == 0) ;
return e ;
}
_info(modinfop)
struct modinfo *modinfop ;
{
return mod_info(&modlinkage, modinfop) ;
}
#endif /* !SYSV */
/* 4.x loadable driver starts here */
#ifndef SYSV
struct dev_ops tcx_ops = {
0, /* revision */
tcx_identify,
tcx_attach,
tcx_open,
tcx_close,
0, 0, 0, 0, 0,
tcx_ioctl,
0,
0,
};
#ifdef LOADABLE
/* structures for loadable driver */
static struct cdevsw tcx_cdev = {
tcx_open, tcx_close, nulldev, nulldev,
tcx_ioctl, nulldev, nulldev, tcx_mmap,
0, 0,
} ;
#ifdef sun4m
static struct vdldrv vd = {
VDMAGIC_DRV, /* Type of module */
"tcx", /* Name of module */
&tcx_ops, /* dev ops vector */
NULL, /* no bdevsw */
&tcx_cdev, /* cdevsw */
0,0, /* major device #. 0 means let system choose */
NULL, /* mb_ctlr structure */
NULL, /* mb_driver structure */
NULL, /* mb_device array */
0, /* number of controllers */
1, /* number of devices */
} ;
#else
static struct vdldrv vd = {
VDMAGIC_DRV, /* Type of module */
"tcx", /* Name of module */
#ifdef sun4c
&tcx_ops, /* dev ops vector */
#else
NULL, /* mb_ctlr structure */
NULL, /* mb_driver structure */
NULL, /* mb_device array */
0, /* number of controllers */
1, /* number of devices */
#endif /* sun4c */
NULL, /* no bdevsw */
&tcx_cdev, /* cdevsw */
0,0, /* major device #. 0 means let system choose */
} ;
#endif /* sun4m */
int
xxxinit(function_code, vdp, vdi, vds)
u_int function_code ;
struct vddrv *vdp ;
addr_t vdi ;
struct vdstat vds ;
{
switch( function_code )
{
case VDLOAD:
vdp->vdd_vdtab = (struct vdlinkage *) &vd ;
return 0 ;
case VDUNLOAD:
return tcx_unload() ;
case VDSTAT:
return 0 ;
default:
return EIO ;
}
}
static int
tcx_unload()
{
register int i ;
register struct tcx_softc *softc;
/* veto if any tcx is open, or interrupt pending */
DEBUGF(7, ("tcx_unload\n"));
for( softc = tcx_softc, i = ntcx; --i >= 0; softc++ ) {
ASRT((softc->vrtflag & ~(TCXCMAPUD|TCXOMAPUD)) == 0 ) ;
if( softc->owner != -1 || HCMISC(softc) & TCX_HCMISC_EN_IRQ )
return EBUSY ;
tcx_unmap_all(softc) ;
}
tcx_kmem_free(tcx_softc, sizeof(struct tcx_softc) * ntcx);
ASRT(total_memory == 0) ;
ntcx=0 ;
next_unit=0 ;
return 0 ;
}
#endif /* LOADABLE */
#endif /* !SYSV */