#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 #include #include #include #include #ifdef SYSV #include #include #include #include #include #include /* for ctob(), used by cg8reg.h */ #else #include #include #include extern int nulldev() ; #endif #ifdef SYSV #include #include #include #include #include #else #include #include #include #include #include #endif SYSV #ifndef SYSV #include #include #include #include #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; indexaddr = 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, ×tamp[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; imaps[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= 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 */ } ; 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 */