/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #pragma weak setintrenable /* * Cgsix theory of operation: * * Most cg6 operations are done by mapping the cg6 components into * user process memory. User processes that share mappings (typically * pixrect programs) must cooperate among themselves to prevent damaging * the state of the cg6. User processes may also acquire private * mappings (MAP_PRIVATE flag to mmap(2)), in which case the cg6 segment * driver will preserve device state for each mapping. * * Note that the segment driver may go away in the future. * * cg6_mmap interprets the device offset as follows: * * CG6_VBASE 0x70000000 * CG6_VADDR_FBC 0x70000000 fbc mapping * CG6_VADDR_TEC 0x70001000 tec mapping * CG6_VADDR_CMAP 0x70002000 colormap dacs * CG6_VADDR_FHC 0x70004000 fhc mapping * CG6_VADDR_THC 0x70005000 thc mapping * CG6_VADDR_ROM 0x70006000 eprom mapping * CG6_VADDR_COLOR 0x70016000 framebuffer mapping * CG6_VADDR_DHC 0x78000000 dac hardware * CG6_VADDR_ALT 0x78002000 alternate registers (?) * CG6_VADDR_UART 0x78004000 uart, if any * CG6_VADDR_VRT 0x78006000 vertical retrace counter page * * The lengths of these mappings should be: * * CG6_CMAP_SZ 0x2000 * CG6_FBCTEC_SZ 0x2000 * CG6_FHCTHC_SZ 0x2000 * CG6_ROM_SZ 0x10000 * CG6_FB_SZ 0x100000 * CG6_DHC_SZ 0x2000 * CG6_ALT_SZ 0x2000 * * Mappings to the fbc and tec registers may be MAP_PRIVATE, in which case * the segment driver keeps a per-context copy of the fbc and tec * registers in local memory. Only one context at a time may have valid * mappings. If a process tries to access the registers through an * invalid mapping, the segment driver in invoked to swap register state * and validate the mappings. * * In the case of the buggy LSC revision 2. chip, the framebuffer mapping * is also considered part of a context. This is to ensure that the * registers are idle before the framebuffer is touched. * * Mappings to FBC, TEC and framebuffer may be made seperately, in which * case the driver uses heuristics to bind seperate mappings into a single * context. These heuristics may break down if mappings are done in a * funny order or in a multi-threaded environment, so seperate mappings * are not recommended. * * Finally, processes have the option of mapping the "vertical retrace * page". This is a page in shared memory containing a 32-bit integer * that is incremented each time a vertical retrace interrupt occurs. It * is used so that programs may synchronize themselves with vertical * retrace. */ /* * SBus accelerated 8 bit color frame buffer driver */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for CG3_MMAP_OFFSET */ #include #include #include #include #include #include #include #include #include #include #include #define KIOIP KSTAT_INTR_PTR(softc->intrstats) #define CG6DEBUG 0 /* configuration options */ #define CG6DELAY(c, n) \ { \ register int N = n; \ while (--N > 0) { \ if (c) \ break; \ drv_usecwait(1); \ } \ } #if CG6DEBUG >= 2 int cg6_debug = 0; #define DEBUGF(level, args) \ { if (cg6_debug >= (level)) cmn_err args; } #define DUMP_SEGS(level, s, c) \ { if (cg6_debug >= (level)) dump_segs(s, c); } #else #define DEBUGF(level, args) /* nothing */ #define DUMP_SEGS(level, s, c) /* nothing */ #endif #define getprop(devi, name, def) \ ddi_getprop(DDI_DEV_T_ANY, (devi), \ DDI_PROP_DONTPASS, (name), (def)) /* config info */ static int cg6_open(dev_t *, int, int, cred_t *); static int cg6_close(dev_t, int, int, cred_t *); static int cg6_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); static int cg6_mmap(dev_t, off_t, int); static int cg6_devmap(dev_t, devmap_cookie_t, offset_t, size_t, size_t *, uint_t); static int cg6_segmap(dev_t, off_t, struct as *, caddr_t *, off_t, uint_t, uint_t, uint_t, cred_t *); static struct vis_identifier cg6_ident = { "SUNWcg6" }; static struct cb_ops cg6_cb_ops = { cg6_open, /* open */ cg6_close, /* close */ nodev, /* strategy */ nodev, /* print */ nodev, /* dump */ nodev, /* read */ nodev, /* write */ cg6_ioctl, /* ioctl */ cg6_devmap, /* devmap */ cg6_mmap, /* mmap */ cg6_segmap, /* segmap */ nochpoll, /* poll */ ddi_prop_op, /* cb_prop_op */ 0, /* streamtab */ D_NEW|D_MP|D_DEVMAP|D_HOTPLUG, /* Driver compatibility flag */ CB_REV, /* rev */ nodev, /* int (*cb_aread)() */ nodev /* int (*cb_awrite)() */ }; static int cg6_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result); static int cg6_attach(dev_info_t *, ddi_attach_cmd_t); static int cg6_detach(dev_info_t *, ddi_detach_cmd_t); static int cg6_power(dev_info_t *, int, int); struct dev_ops cgsix_ops = { DEVO_REV, /* devo_rev, */ 0, /* refcnt */ cg6_info, /* info */ nulldev, /* identify */ nulldev, /* probe */ cg6_attach, /* attach */ cg6_detach, /* detach */ nodev, /* reset */ &cg6_cb_ops, /* driver operations */ (struct bus_ops *)0, /* bus operations */ cg6_power }; /* * This stucture is used to contain the driver * private mapping data (one for each requested * device mapping). A pointer to this data is * passed into each mapping callback routine. */ struct cg6map_pvt { struct cg6_softc *softc; devmap_cookie_t dhp; /* handle of devmap object */ uint_t type; /* mapping type */ off_t offset; /* starting offset of this map */ size_t len; /* length of this map */ struct cg6_cntxt *context; /* associated context */ struct cg6map_pvt *next; /* List of associated pvt's for */ /* this context */ }; static struct ddi_device_acc_attr endian_attr = { DDI_DEVICE_ATTR_V0, DDI_NEVERSWAP_ACC, DDI_STRICTORDER_ACC }; /* how much to map */ #define CG6MAPSIZE MMAPSIZE(0) /* vertical retrace counter page */ #ifndef CG6_VRT_SZ #define CG6_VRT_SZ 8192 #endif /* boardrev bits */ #define BRDRV_SETRES 0x01 /* board supports set resolution */ #define BRDRV_TYPE 0x78 /* board type: */ #define BRDRV_GX 0x00 #define BRDRV_LSC 0x08 #define BRDRV_DUPLO 0x10 #define BRDRV_LEGOHR 0x18 #define BRDRV_QAUDRO 0x20 #define BRDRV_HIRES 0x80 /* hires dacs (junior vs. senior) */ #define CG6_FBC_WAIT 500000 /* .5 seconds */ /* enable/disable interrupt */ #define TEC_EN_VBLANK_IRQ 0x20 #define TEC_HCMISC_IRQBIT 0x10 /* position value to use to disable HW cursor */ #define CG6_CURSOR_OFFPOS ((uint_t)0xffe0ffe0) /* * Per-context info: * many registers in the tec and fbc do * not need to be saved/restored. */ struct cg6_cntxt { struct cg6_cntxt *link; /* link to next (private) context if any */ struct cg6map_pvt *pvt; /* List of associated pvt's for this context */ pid_t pid; /* "owner" of this context */ int flag; struct { uint_t mv; uint_t clip; uint_t vdc; uint_t data[64][2]; } tec; struct { uint_t status; uint_t clipcheck; struct l_fbc_misc misc; uint_t x0, y0, x1, y1, x2, y2, x3, y3; uint_t rasteroffx, rasteroffy; uint_t autoincx, autoincy; uint_t clipminx, clipminy, clipmaxx, clipmaxy; uint_t fcolor, bcolor; struct l_fbc_rasterop rasterop; uint_t planemask, pixelmask; union l_fbc_pattalign pattalign; uint_t pattern0, pattern1, pattern2, pattern3, pattern4, pattern5, pattern6, pattern7; } fbc; }; /* per-unit data */ struct cg6_softc { 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 size_t size; /* total size of frame buffer */ size_t ndvramsz; /* size of non-display Video RAM */ caddr_t ndvram; /* Storage for nd-VRAM, while suspended */ size_t dummysize; /* total size of overlay plane */ kmutex_t interlock; /* interrupt locking */ off_t addr_rom; /* varies between p4 & sbus */ caddr_t fbctec; /* fbc&tec kernel map addr. */ caddr_t cmap; /* colormap kernel map addr. */ caddr_t fhcthc; /* fhc&thc kernel map addr. */ caddr_t rom; /* rom kernel map addr. */ caddr_t dhc; /* dac hardware */ caddr_t alt; /* alt registers */ caddr_t uart; /* uart registers */ pfn_t fbpfnum; /* pfn of fb for mmap() */ 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 */ uint32_t image[32]; /* cursor image bitmap */ uint32_t mask[32]; /* cursor mask bitmap */ } cur; union { /* shadow overlay color map */ uint32_t omap_int[2]; /* cheating here to save space */ uchar_t omap_char[3][2]; } omap_image; #define omap_rgb omap_image.omap_char[0] ushort_t omap_update; /* overlay colormap update flag */ uint32_t cmap_index; /* colormap update index */ uint32_t cmap_count; /* colormap update count */ union { /* shadow color map */ uint32_t cmap_int[CG6_CMAP_ENTRIES * 3 / sizeof (uint32_t)]; uchar_t cmap_char[3][CG6_CMAP_ENTRIES]; } cmap_image; #define cmap_rgb cmap_image.cmap_char[0] #define CG6VRTIOCTL 1 /* FBIOVERTICAL in effect */ #define CG6VRTCTR 2 /* OWGX vertical retrace counter */ size_t fbmappable; /* bytes mappable */ int *vrtpage; /* pointer to VRT page */ ddi_umem_cookie_t vrtcookie; /* pointer to VRT allocation */ int vrtmaps; /* number of VRT page maps */ int vrtflag; /* vrt interrupt flag */ struct cg6_info cg6info; /* info about this cg6 */ struct mon_info moninfo; /* info about this monitor */ struct cg6_cntxt *curctx; /* context switching */ struct cg6_cntxt shared_ctx; /* shared context */ struct cg6_cntxt *pvt_ctx; /* list of non-shared contexts */ int chiprev; /* fbc chip revision # */ int emulation; /* emulation type, normally cgsix */ dev_info_t *devi; /* back pointer */ ddi_iblock_cookie_t iblock_cookie; /* block interrupts */ kmutex_t mutex; /* mutex locking */ kcondvar_t vrtsleep; /* for waiting on vertical retrace */ int mapped_by_prom; /* $#!@ SVr4 */ off_t mapped_by_driver; int waiting; int cg6_suspended; /* true if driver is suspended */ int vidon; /* video enable state */ int intr_flag; kstat_t *intrstats; /* interrupt statistics */ }; static int cg6map_map(devmap_cookie_t, dev_t, uint_t, offset_t, size_t, void **); static int cg6map_contextmgt(devmap_cookie_t, void *, offset_t, size_t, uint_t, uint_t); static int cg6map_dup(devmap_cookie_t, void *, devmap_cookie_t, void **); static void cg6map_unmap(devmap_cookie_t, void *, offset_t, size_t, devmap_cookie_t, void **, devmap_cookie_t, void **); static int cg6map_access(devmap_cookie_t, void *, offset_t, size_t, uint_t, uint_t); static struct devmap_callback_ctl cg6map_ops = { DEVMAP_OPS_REV, /* devmap_ops version number */ cg6map_map, /* devmap_ops map routine */ cg6map_access, /* devmap_ops access routine */ cg6map_dup, /* devmap_ops dup routine */ cg6map_unmap, /* devmap_ops unmap routine */ }; static size_t pagesize; static void *cg6_softc_head; clock_t cg6_ctxholdval = 1; /* default structure for FBIOGATTR ioctl */ static struct fbgattr cg6_attr = { /* real_type owner */ FBTYPE_SUNFAST_COLOR, 0, /* fbtype: type h w depth cms size */ {FBTYPE_SUNFAST_COLOR, 0, 0, CG6_DEPTH, CG6_CMAP_ENTRIES, 0}, /* fbsattr: flags emu_type dev_specific */ {0, FBTYPE_SUN4COLOR, {0}}, /* emu_types */ {FBTYPE_SUNFAST_COLOR, FBTYPE_SUN3COLOR, FBTYPE_SUN4COLOR, -1} }; /* * handy macros */ #define getsoftc(instance) \ ((struct cg6_softc *)ddi_get_soft_state(cg6_softc_head, (instance))) #define btob(n) ptob(btopr(n)) /* TODO, change this? */ /* convert softc to data pointers */ #define S_FBC(softc) ((struct fbc *)(softc)->fbctec) #define S_TEC(softc) ((struct tec *)((softc)->fbctec + CG6_TEC_POFF)) #define S_FHC(softc) ((uint_t *)(softc)->fhcthc) #define S_THC(softc) ((struct thc *)((softc)->fhcthc + CG6_TEC_POFF)) #define S_CMAP(softc) ((struct cg6_cmap *)(softc)->cmap) #define cg6_set_video(softc, on) thc_set_video(S_THC(softc), (on)) #define cg6_get_video(softc) thc_get_video(S_THC(softc)) #define cg6_int_enable(softc) \ {\ thc_int_enable(S_THC(softc)); } #define cg6_int_disable_intr(softc) \ {\ thc_int_disable(S_THC(softc)); } #define cg6_int_disable(softc) \ {\ mutex_enter(&(softc)->interlock); \ softc->intr_flag = 1; \ cg6_int_disable_intr(softc); \ mutex_exit(&(softc)->interlock); } #define cg6_int_pending(softc) thc_int_pending(S_THC(softc)) /* check if color map update is pending */ #define cg6_update_pending(softc) \ ((softc)->cmap_count || (softc)->omap_update) /* * forward references */ static uint_t cg6_intr(caddr_t); static void cg6_reset_cmap(volatile uchar_t *, uint_t); static void cg6_update_cmap(struct cg6_softc *, uint_t, uint_t); static void cg6_cmap_bcopy(uchar_t *, uchar_t *, uint_t); static void cg6_restore_prom_cmap(struct cg6_softc *, volatile uchar_t *, uint_t); static void cg6_setcurpos(struct cg6_softc *); static void cg6_setcurshape(struct cg6_softc *); static void cg6_reset(struct cg6_softc *); static int cg6_cntxsave(volatile struct fbc *, volatile struct tec *, struct cg6_cntxt *); static int cg6_cntxrestore(volatile struct fbc *, volatile struct tec *, struct cg6_cntxt *); static struct cg6_cntxt *ctx_map_insert(struct cg6_softc *, int); static pid_t getpid(void); /* Loadable Driver stuff */ static struct modldrv modldrv = { &mod_driverops, /* Type of module. This one is a driver */ "cgsix driver v%I%", /* Name of the module. */ &cgsix_ops, /* driver ops */ }; static struct modlinkage modlinkage = { MODREV_1, (void *) &modldrv, NULL }; int _init(void) { register int e; if ((e = ddi_soft_state_init(&cg6_softc_head, sizeof (struct cg6_softc), 1)) != 0) { DEBUGF(1, (CE_CONT, "done\n")); return (e); } e = mod_install(&modlinkage); if (e) { ddi_soft_state_fini(&cg6_softc_head); DEBUGF(1, (CE_CONT, "done\n")); } DEBUGF(1, (CE_CONT, "cgsix: _init done rtn=%d\n", e)); return (e); } int _fini(void) { register int e; DEBUGF(1, (CE_CONT, "cgsix: _fini, mem used=%d\n", total_memory)); if ((e = mod_remove(&modlinkage)) != 0) return (e); ddi_soft_state_fini(&cg6_softc_head); return (0); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } static int cg6_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) { register struct cg6_softc *softc; caddr_t reg; int w, h, bytes; char *tmp; char name[16]; int unit = ddi_get_instance(devi); int proplen; caddr_t fb_ndvram; DEBUGF(1, (CE_CONT, "cg6_attach unit=%d cmd=%d\n", unit, (int)cmd)); switch (cmd) { case DDI_ATTACH: break; case DDI_RESUME: if ((softc = ddi_get_driver_private(devi)) == NULL) return (DDI_FAILURE); if (!softc->cg6_suspended) return (DDI_SUCCESS); mutex_enter(&softc->mutex); cg6_reset(softc); if (softc->curctx) { /* Restore the video state */ cg6_set_video(softc, softc->vidon); /* Restore non display RAM */ if (ddi_map_regs(devi, 0, (caddr_t *)&fb_ndvram, CG6_ADDR_COLOR + softc->_w * softc->_h, softc->ndvramsz) == -1) { mutex_exit(&softc->mutex); return (DDI_FAILURE); } bcopy(softc->ndvram, fb_ndvram, softc->ndvramsz); ddi_unmap_regs(devi, 0, (caddr_t *)&fb_ndvram, CG6_ADDR_COLOR + softc->_w * softc->_h, softc->ndvramsz); kmem_free(softc->ndvram, softc->ndvramsz); /* Restore other frame buffer state */ (void) cg6_cntxrestore(S_FBC(softc), S_TEC(softc), softc->curctx); cg6_setcurpos(softc); cg6_setcurshape(softc); cg6_update_cmap(softc, (uint_t)_ZERO_, CG6_CMAP_ENTRIES); cg6_int_enable(softc); /* Schedule the update */ } softc->cg6_suspended = 0; mutex_exit(&softc->mutex); return (DDI_SUCCESS); default: return (DDI_FAILURE); } DEBUGF(1, (CE_CONT, "cg6_attach unit=%d\n", unit)); pagesize = (size_t)ddi_ptob(devi, 1); /* Allocate softc struct */ if (ddi_soft_state_zalloc(cg6_softc_head, unit) != 0) { return (DDI_FAILURE); } softc = getsoftc(unit); /* link it in */ softc->devi = devi; DEBUGF(1, (CE_CONT, "cg6_attach devi=0x%x unit=%d\n", devi, unit)); ddi_set_driver_private(devi, softc); /* Grab properties from PROM */ /* TODO don't really want default w, h */ if (ddi_prop_op(DDI_DEV_T_ANY, devi, PROP_LEN_AND_VAL_ALLOC, DDI_PROP_DONTPASS, "emulation", (caddr_t)&tmp, &proplen) == DDI_PROP_SUCCESS) { if (strcmp(tmp, "cgthree+") == 0) softc->emulation = FBTYPE_SUN3COLOR; else if (strcmp(tmp, "cgfour+") == 0) softc->emulation = FBTYPE_SUN4COLOR; else if (strcmp(tmp, "bwtwo+") == 0) softc->emulation = FBTYPE_SUN2BW; else softc->emulation = FBTYPE_SUNFAST_COLOR; kmem_free(tmp, proplen); } else softc->emulation = FBTYPE_SUNFAST_COLOR; softc->_w = w = getprop(devi, "width", 1152); softc->_h = h = getprop(devi, "height", 900); bytes = getprop(devi, "linebytes", mpr_linebytes(w, 8)); softc->_linebytes = bytes; /* Compute size of color frame buffer */ bytes = btob(bytes * h); softc->size = (size_t)ddi_ptob(devi, ddi_btopr(devi, bytes)); softc->cg6info.vmsize = getprop(devi, "vmsize", 1); if (softc->cg6info.vmsize > 1) { softc->size = (size_t)ddi_ptob(devi, ddi_btopr(devi, 8 * 1024 * 1024)); softc->fbmappable = 8 * 1024 * 1024; } else softc->fbmappable = 1024 * 1024; /* Compute size of dummy overlay/enable planes */ softc->dummysize = btob(mpr_linebytes(w, 1) * h) * 2; /* * only use address property if we are console fb NOTE: if the prom has * already mapped the fb *and* it has mapped all of fbmappable, then we * don't need a new mapping */ if (reg = (caddr_t)(uintptr_t)getprop(devi, "address", 0)) { softc->_fb = (MPR_T *) reg; softc->mapped_by_prom = 1; if (ddi_ptob(devi, ddi_btopr(devi, w * h)) <= getprop(devi, "fbmapped", w * h)) bytes = 0; DEBUGF(2, (CE_CONT, "cg6 mapped by PROM\n")); } softc->cg6info.line_bytes = softc->_linebytes; softc->cg6info.accessible_width = getprop(devi, "awidth", 1152); softc->cg6info.accessible_height = (uint_t) (softc->cg6info.vmsize * 1024 * 1024) / softc->cg6info.accessible_width; softc->cg6info.hdb_capable = getprop(devi, "dblbuf", 0); softc->cg6info.boardrev = getprop(devi, "boardrev", 0); softc->vrtpage = NULL; softc->vrtmaps = 0; softc->vrtflag = 0; #ifdef DEBUG softc->cg6info.pad1 = CG6_VADDR_COLOR + CG6_FB_SZ; #endif /* * get monitor attributes */ softc->moninfo.mon_type = getprop(devi, "montype", 0); softc->moninfo.pixfreq = getprop(devi, "pixfreq", 929405); softc->moninfo.hfreq = getprop(devi, "hfreq", 61795); softc->moninfo.vfreq = getprop(devi, "vfreq", 66); softc->moninfo.hfporch = getprop(devi, "hfporch", 32); softc->moninfo.vfporch = getprop(devi, "vfporch", 2); softc->moninfo.hbporch = getprop(devi, "hbporch", 192); softc->moninfo.vbporch = getprop(devi, "vbporch", 31); softc->moninfo.hsync = getprop(devi, "hsync", 128); softc->moninfo.vsync = getprop(devi, "vsync", 4); /* * map in the registers. Map fbc&tec together. Likewise for fhc&thc. */ softc->addr_rom = CG6_ADDR_ROM_SBUS; if (ddi_map_regs(devi, 0, &softc->fbctec, CG6_ADDR_FBC, (off_t)CG6_FBCTEC_SZ) != 0) { (void) cg6_detach(devi, DDI_DETACH); return (DDI_FAILURE); } if (ddi_map_regs(devi, 0, &softc->cmap, CG6_ADDR_CMAP, (off_t)CG6_CMAP_SZ) != 0) { (void) cg6_detach(devi, DDI_DETACH); return (DDI_FAILURE); } if (ddi_map_regs(devi, 0, &softc->fhcthc, CG6_ADDR_FHC, (off_t)CG6_FHCTHC_SZ) != 0) { (void) cg6_detach(devi, DDI_DETACH); return (DDI_FAILURE); } softc->chiprev = *S_FHC(softc) >> FHC_CONFIG_REV_SHIFT & FHC_CONFIG_REV_MASK; cg6_reset(softc); if (ddi_get_iblock_cookie(devi, 0, &softc->iblock_cookie) != DDI_SUCCESS) { DEBUGF(2, (CE_CONT, "cg6_attach%d ddi_get_iblock_cookie failed\n", unit)); (void) cg6_detach(devi, DDI_DETACH); return (DDI_FAILURE); } mutex_init(&softc->interlock, NULL, MUTEX_DRIVER, softc->iblock_cookie); mutex_init(&softc->mutex, NULL, MUTEX_DRIVER, softc->iblock_cookie); cv_init(&softc->vrtsleep, NULL, CV_DRIVER, NULL); if (ddi_add_intr(devi, 0, &softc->iblock_cookie, 0, cg6_intr, (caddr_t)softc) != DDI_SUCCESS) { DEBUGF(2, (CE_CONT, "cg6_attach%d add_intr failed\n", unit)); (void) cg6_detach(devi, DDI_DETACH); return (DDI_FAILURE); } /* * Initialize hardware colormap and software colormap images. It might * make sense to read the hardware colormap here. */ cg6_reset_cmap(softc->cmap_rgb, CG6_CMAP_ENTRIES); cg6_reset_cmap(softc->omap_rgb, 2); cg6_update_cmap(softc, (uint_t)_ZERO_, CG6_CMAP_ENTRIES); cg6_update_cmap(softc, (uint_t)_ZERO_, (uint_t)_ZERO_); DEBUGF(2, (CE_CONT, "cg6_attach%d just before create_minor node\n", unit)); (void) sprintf(name, "cgsix%d", unit); if (ddi_create_minor_node(devi, name, S_IFCHR, unit, DDI_NT_DISPLAY, NULL) == DDI_FAILURE) { ddi_remove_minor_node(devi, NULL); DEBUGF(2, (CE_CONT, "cg6_attach%d create_minor node failed\n", unit)); return (DDI_FAILURE); } ddi_report_dev(devi); if (softc->chiprev == 0) cmn_err(CE_CONT, "?Revision 0 FBC\n"); cmn_err(CE_CONT, "?cgsix%d: screen %dx%d, %s buffered, %dM mappable, rev %d\n", unit, w, h, softc->cg6info.hdb_capable ? "double" : "single", softc->cg6info.vmsize, softc->chiprev); softc->pvt_ctx = NULL; /* * Initialize power management bookkeeping; components are created idle */ if (pm_create_components(devi, 2) == DDI_SUCCESS) { (void) pm_busy_component(devi, 0); pm_set_normal_power(devi, 0, 1); pm_set_normal_power(devi, 1, 1); (void) sprintf(name, "cgsixc%d", unit); softc->intrstats = kstat_create("cgsix", unit, name, "controller", KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT); if (softc->intrstats) { kstat_install(softc->intrstats); } return (DDI_SUCCESS); } else { return (DDI_FAILURE); } } static int cg6_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) { int instance = ddi_get_instance(devi); register struct cg6_softc *softc = getsoftc(instance); caddr_t fb_ndvram; DEBUGF(1, (CE_CONT, "cg6_detach softc=%x, devi=0x%x\n", softc, devi)); switch (cmd) { case DDI_DETACH: break; case DDI_SUSPEND: if (softc == NULL) return (DDI_FAILURE); if (softc->cg6_suspended) return (DDI_FAILURE); mutex_enter(&softc->mutex); if (softc->curctx) { struct fbc *fbc0 = S_FBC(softc); /* Save the video state */ softc->vidon = cg6_get_video(softc); /* Save non display RAM */ softc->ndvramsz = (softc->cg6info.vmsize * 1024 * 1024) - (softc->_w * softc->_h); if ((softc->ndvram = kmem_alloc(softc->ndvramsz, KM_NOSLEEP)) == NULL) { mutex_exit(&softc->mutex); return (DDI_FAILURE); } /* * If FBC is busy, wait for maximum of 2 seconds for it * to be idle */ CG6DELAY(!(fbc0->l_fbc_status & L_FBC_BUSY), 4*CG6_FBC_WAIT); if (fbc0->l_fbc_status & L_FBC_BUSY) { /* * if still busy, try another 2 seconds before * giving up */ CG6DELAY(!(fbc0->l_fbc_status & L_FBC_BUSY), 4*CG6_FBC_WAIT); if (fbc0->l_fbc_status & L_FBC_BUSY) cmn_err(CE_WARN, "cg6_detach: FBC still busy"); } if (ddi_map_regs(devi, 0, &fb_ndvram, CG6_ADDR_COLOR + softc->_w * softc->_h, softc->ndvramsz) == -1) { kmem_free(softc->ndvram, softc->ndvramsz); mutex_exit(&softc->mutex); return (DDI_FAILURE); } bcopy(fb_ndvram, softc->ndvram, softc->ndvramsz); ddi_unmap_regs(devi, 0, &fb_ndvram, CG6_ADDR_COLOR + softc->_w * softc->_h, softc->ndvramsz); /* Save other frame buffer state */ (void) cg6_cntxsave(S_FBC(softc), S_TEC(softc), softc->curctx); } softc->cg6_suspended = 1; mutex_exit(&softc->mutex); return (DDI_SUCCESS); default: return (DDI_FAILURE); } /* shut off video if not console */ if (!softc->mapped_by_prom) cg6_set_video(softc, 0); mutex_enter(&softc->mutex); cg6_int_disable(softc); mutex_exit(&softc->mutex); ddi_remove_intr(devi, 0, softc->iblock_cookie); if (softc->fbctec) ddi_unmap_regs(devi, 0, &softc->fbctec, CG6_ADDR_FBC, CG6_FBCTEC_SZ); if (softc->cmap) ddi_unmap_regs(devi, 0, &softc->cmap, CG6_ADDR_CMAP, CG6_CMAP_SZ); if (softc->fhcthc) ddi_unmap_regs(devi, 0, &softc->fhcthc, CG6_ADDR_FHC, CG6_FHCTHC_SZ); if (softc->intrstats) { kstat_delete(softc->intrstats); } softc->intrstats = NULL; if (softc->vrtpage != NULL) ddi_umem_free(softc->vrtcookie); mutex_destroy(&softc->mutex); cv_destroy(&softc->vrtsleep); ASSERT(softc->curctx == NULL); /* free softc struct */ (void) ddi_soft_state_free(cg6_softc_head, instance); pm_destroy_components(devi); return (DDI_SUCCESS); } static int cg6_power(dev_info_t *dip, int cmpt, int level) { struct cg6_softc *softc; /* * Framebuffer is represented by cmpt 0. In cg6, no power * management is done on the framebuffer itself. Only the * monitor (cmpt 1) is being power managed. */ if (cmpt == 0) return (DDI_SUCCESS); if (cmpt != 1 || 0 > level || level > 1 || (softc = ddi_get_driver_private(dip)) == NULL) return (DDI_FAILURE); if (level) { /* Turn on sync and video. */ mutex_enter(&softc->mutex); S_THC(softc)->l_thc_hcmisc |= THC_HCMISC_RESET; drv_usecwait(500); S_THC(softc)->l_thc_hcmisc |= (THC_HCMISC_SYNCEN | THC_HCMISC_VIDEO); S_THC(softc)->l_thc_hcmisc &= ~THC_HCMISC_RESET; cg6_update_cmap(softc, (uint_t)_ZERO_, CG6_CMAP_ENTRIES); cg6_int_enable(softc); mutex_exit(&softc->mutex); } else { /* Turn off sync and video. */ mutex_enter(&softc->mutex); S_THC(softc)->l_thc_hcmisc |= THC_HCMISC_RESET; drv_usecwait(500); S_THC(softc)->l_thc_hcmisc &= ~(THC_HCMISC_VIDEO | THC_HCMISC_SYNCEN); S_THC(softc)->l_thc_hcmisc &= ~THC_HCMISC_RESET; mutex_exit(&softc->mutex); } return (DDI_SUCCESS); } /* ARGSUSED */ static int cg6_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) { int error = DDI_SUCCESS; minor_t instance; struct cg6_softc *softc; instance = getminor((dev_t)arg); switch (infocmd) { case DDI_INFO_DEVT2DEVINFO: if ((softc = getsoftc(instance)) == NULL) { error = DDI_FAILURE; } else { *result = (void *) softc->devi; error = DDI_SUCCESS; } break; case DDI_INFO_DEVT2INSTANCE: *result = (void *) (uintptr_t)instance; error = DDI_SUCCESS; break; default: error = DDI_FAILURE; } return (error); } /*ARGSUSED*/ static int cg6_open(dev_t *devp, int flag, int otyp, cred_t *cred) { int unit = getminor(*devp); struct cg6_softc *softc = getsoftc(unit); int error = 0; DEBUGF(2, (CE_CONT, "cg6_open(%d), mem used=%d\n", unit, total_memory)); /* * is this gorp necessary? */ if (otyp != OTYP_CHR) { error = EINVAL; } else if (softc == NULL) { error = ENXIO; } return (error); } /*ARGSUSED*/ static int cg6_close(dev_t dev, int flag, int otyp, cred_t *cred) { int unit = getminor(dev); struct cg6_softc *softc = getsoftc(unit); int error = 0; DEBUGF(2, (CE_CONT, "cg6_close(%d, %d, %d), mem used=%d\n", unit, flag, otyp, total_memory)); if (otyp != OTYP_CHR) { error = EINVAL; } else if (softc == NULL) { error = ENXIO; } else { mutex_enter(&softc->mutex); cg6_reset_cmap(softc->cmap_rgb, CG6_CMAP_ENTRIES); cg6_restore_prom_cmap(softc, softc->cmap_rgb, CG6_CMAP_ENTRIES); softc->cur.enable = 0; softc->curctx = NULL; cg6_reset(softc); mutex_exit(&softc->mutex); } return (error); } /*ARGSUSED*/ static int cg6_mmap(dev_t dev, off_t off, int prot) { struct cg6_softc *softc = getsoftc(getminor(dev)); ssize_t diff; caddr_t page; intptr_t rval = 0; DEBUGF(off ? 5 : 1, (CE_CONT, "cg6_mmap(%d, 0x%x)\n", getminor(dev), (uint_t)off)); if ((diff = off - CG6_VADDR_COLOR) >= 0 && diff < softc->fbmappable) rval = softc->fbpfnum + diff / pagesize; else if ((diff = off - CG6_VADDR_FBC) >= 0 && diff < CG6_FBCTEC_SZ) page = softc->fbctec + diff; else if ((diff = off - CG6_VADDR_CMAP) >= 0 && diff < CG6_CMAP_SZ) page = softc->cmap + diff; else if ((diff = off - CG6_VADDR_FHC) >= 0 && diff < CG6_FHCTHC_SZ) page = softc->fhcthc + diff; else if ((diff = off - CG6_VADDR_ROM) >= 0 && diff < CG6_ROM_SZ) page = softc->rom + diff; else if ((diff = off - CG6_VADDR_DHC) >= 0 && diff < CG6_DHC_SZ) page = softc->dhc + diff; else if ((diff = off - CG6_VADDR_ALT) >= 0 && diff < CG6_ALT_SZ) page = softc->alt + diff; else if ((diff = off - CG6_VADDR_VRT) >= 0 && diff < CG6_VRT_SZ) page = softc->vrtpage ? (caddr_t)softc->vrtpage + diff : (caddr_t)-1; else if ((diff = off - CG3_MMAP_OFFSET) >= 0 && diff < softc->fbmappable) rval = softc->fbpfnum + diff / pagesize; else if (off < CG6_VBASE) { /* * getting more and more complicated; what we return depends on what * we're emulating */ if (softc->emulation == FBTYPE_SUN3COLOR) { if (off >= 0 && off < softc->fbmappable) rval = softc->fbpfnum + diff / pagesize; else page = (caddr_t)-1; } else { /* softc->emulation == FBTYPE_SUN4COLOR */ if (off >= 0 && off < softc->dummysize) page = softc->rom; else if ((diff = off - softc->dummysize) < softc->fbmappable) rval = softc->fbpfnum + diff / pagesize; } /* TODO: bw2? */ } else page = (caddr_t)-1; if (rval == 0) if (page != (caddr_t)-1) rval = hat_getkpfnum(page); else rval = -1; DEBUGF(5, (CE_CONT, "cg6_mmap returning 0x%x\n", rval)); return ((int)rval); /* XXX64 */ } /*ARGSUSED*/ static int cg6_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *cred, int *rval) { struct cg6_softc *softc = getsoftc(getminor(dev)); int cursor_cmap; int i; uchar_t *iobuf_cmap_red; uchar_t *iobuf_cmap_green; uchar_t *iobuf_cmap_blue; uchar_t *stack_cmap; STRUCT_DECL(fbcmap, fbcmap); STRUCT_DECL(fbcursor, fbcursor); uint_t index; uint_t count; uchar_t *map; uint_t entries; DEBUGF(3, (CE_CONT, "cg6_ioctl(%d, 0x%x)\n", getminor(dev), cmd)); /* default to updating normal colormap */ cursor_cmap = 0; switch (cmd) { case VIS_GETIDENTIFIER: if (ddi_copyout((caddr_t)&cg6_ident, (caddr_t)data, sizeof (struct vis_identifier), mode)) return (EFAULT); break; case FBIOPUTCMAP: case FBIOGETCMAP: cmap_ioctl: if (cursor_cmap == 0) { STRUCT_INIT(fbcmap, mode); if (ddi_copyin((caddr_t)data, STRUCT_BUF(fbcmap), STRUCT_SIZE(fbcmap), mode)) return (EFAULT); } index = STRUCT_FGET(fbcmap, index); count = STRUCT_FGET(fbcmap, count); if (count == 0) { return (0); } if (cursor_cmap == 0) { switch (PIX_ATTRGROUP(index)) { case 0: case PIXPG_8BIT_COLOR: map = softc->cmap_rgb; entries = CG6_CMAP_ENTRIES; break; default: return (EINVAL); } } else { map = softc->omap_rgb; entries = 2; } if ((index &= PIX_ALL_PLANES) >= entries || index + count > entries) { return (EINVAL); } /* * Allocate memory for color map RGB entries. */ stack_cmap = kmem_alloc((CG6_CMAP_ENTRIES * 3), KM_SLEEP); iobuf_cmap_red = stack_cmap; iobuf_cmap_green = stack_cmap + CG6_CMAP_ENTRIES; iobuf_cmap_blue = stack_cmap + (CG6_CMAP_ENTRIES * 2); if (cmd == FBIOPUTCMAP) { int error; DEBUGF(3, (CE_CONT, "FBIOPUTCMAP\n")); if (error = ddi_copyin( STRUCT_FGETP(fbcmap, red), iobuf_cmap_red, count, mode)) { kmem_free(stack_cmap, (CG6_CMAP_ENTRIES * 3)); return (error); } if (error = ddi_copyin( STRUCT_FGETP(fbcmap, green), iobuf_cmap_green, count, mode)) { kmem_free(stack_cmap, (CG6_CMAP_ENTRIES * 3)); return (error); } if (error = ddi_copyin( STRUCT_FGETP(fbcmap, blue), iobuf_cmap_blue, count, mode)) { kmem_free(stack_cmap, (CG6_CMAP_ENTRIES * 3)); return (error); } mutex_enter(&softc->mutex); map += index * 3; if (cg6_update_pending(softc)) cg6_int_disable(softc); /* * Copy color map entries from stack to the color map * table in the softc area. */ cg6_cmap_bcopy(iobuf_cmap_red, map++, count); cg6_cmap_bcopy(iobuf_cmap_green, map++, count); cg6_cmap_bcopy(iobuf_cmap_blue, map, count); /* cursor colormap update */ if (entries < CG6_CMAP_ENTRIES) count = 0; cg6_update_cmap(softc, index, count); cg6_int_enable(softc); mutex_exit(&softc->mutex); } else { /* FBIOGETCMAP */ DEBUGF(3, (CE_CONT, "FBIOGETCMAP\n")); mutex_enter(&softc->mutex); map += index * 3; /* * Copy color map entries from soft area to * local storage and prepare for a copyout */ cg6_cmap_bcopy(iobuf_cmap_red, map++, -count); cg6_cmap_bcopy(iobuf_cmap_green, map++, -count); cg6_cmap_bcopy(iobuf_cmap_blue, map, -count); mutex_exit(&softc->mutex); if (ddi_copyout(iobuf_cmap_red, STRUCT_FGETP(fbcmap, red), count, mode)) { kmem_free(stack_cmap, (CG6_CMAP_ENTRIES * 3)); return (EFAULT); } if (ddi_copyout(iobuf_cmap_green, STRUCT_FGETP(fbcmap, green), count, mode)) { kmem_free(stack_cmap, (CG6_CMAP_ENTRIES * 3)); return (EFAULT); } if (ddi_copyout(iobuf_cmap_blue, STRUCT_FGETP(fbcmap, blue), count, mode)) { kmem_free(stack_cmap, (CG6_CMAP_ENTRIES * 3)); return (EFAULT); } } kmem_free(stack_cmap, (CG6_CMAP_ENTRIES * 3)); break; case FBIOSATTR: { struct fbsattr attr; if (ddi_copyin((caddr_t)data, (caddr_t)&attr, sizeof (attr), mode)) return (EFAULT); DEBUGF(3, (CE_CONT, "FBIOSATTR, type=%d\n", attr.emu_type)); if (attr.emu_type != -1) switch (attr.emu_type) { case FBTYPE_SUN3COLOR: case FBTYPE_SUN4COLOR: case FBTYPE_SUN2BW: case FBTYPE_SUNFAST_COLOR: mutex_enter(&softc->mutex); softc->emulation = attr.emu_type; mutex_exit(&softc->mutex); break; default: return (EINVAL); } /* ignore device-dependent stuff */ } break; case FBIOGATTR: { struct fbgattr attr; DEBUGF(3, (CE_CONT, "FBIOGATTR, emu_type=%d\n", softc->emulation)); bcopy((caddr_t)&cg6_attr, (caddr_t)&attr, sizeof (attr)); mutex_enter(&softc->mutex); attr.fbtype.fb_type = softc->emulation; attr.fbtype.fb_width = softc->_w; attr.fbtype.fb_height = softc->_h; /* XXX not quite like a cg4 */ attr.fbtype.fb_size = (int)softc->size; attr.sattr.emu_type = softc->emulation; mutex_exit(&softc->mutex); if (ddi_copyout((caddr_t)&attr, (caddr_t)data, sizeof (struct fbgattr), mode)) return (EFAULT); } break; /* * always claim to be a cg4 if they call this ioctl. This is to * support older software which was staticly-linked before cg6 was * invented, and to support newer software which has come to expect * this behavior. */ case FBIOGTYPE: { struct fbtype fb; mutex_enter(&softc->mutex); bcopy(&cg6_attr.fbtype, &fb, sizeof (struct fbtype)); DEBUGF(3, (CE_CONT, "FBIOGTYPE\n")); fb.fb_type = FBTYPE_SUN4COLOR; fb.fb_width = softc->_w; fb.fb_height = softc->_h; /* XXX not quite like a cg4 */ fb.fb_size = (int)softc->size; mutex_exit(&softc->mutex); if (ddi_copyout((caddr_t)&fb, (caddr_t)data, sizeof (struct fbtype), mode)) return (EFAULT); } break; case FBIOSVIDEO: DEBUGF(3, (CE_CONT, "FBIOSVIDEO\n")); if (ddi_copyin((caddr_t)data, (caddr_t)&i, sizeof (int), mode)) return (EFAULT); mutex_enter(&softc->mutex); cg6_set_video(softc, i & FBVIDEO_ON); mutex_exit(&softc->mutex); break; case FBIOGVIDEO: DEBUGF(3, (CE_CONT, "FBIOGVIDEO\n")); mutex_enter(&softc->mutex); i = cg6_get_video(softc) ? FBVIDEO_ON : FBVIDEO_OFF; mutex_exit(&softc->mutex); if (ddi_copyout((caddr_t)&i, (caddr_t)data, sizeof (int), mode)) return (EFAULT); break; /* informational ioctls */ case FBIOGXINFO: if (ddi_copyout((caddr_t)&softc->cg6info, (caddr_t)data, sizeof (struct cg6_info), mode)) return (EFAULT); return (0); case FBIOMONINFO: if (ddi_copyout((caddr_t)&softc->moninfo, (caddr_t)data, sizeof (struct mon_info), mode)) return (EFAULT); return (0); /* vertical retrace interrupt */ case FBIOVERTICAL: mutex_enter(&softc->mutex); softc->vrtflag |= CG6VRTIOCTL; cg6_int_enable(softc); cv_wait(&softc->vrtsleep, &softc->mutex); mutex_exit(&softc->mutex); return (0); case FBIOVRTOFFSET: i = CG6_VADDR_VRT; if (ddi_copyout((caddr_t)&i, (caddr_t)data, sizeof (int), mode)) return (EFAULT); return (0); /* HW cursor control */ case FBIOSCURSOR: { int set; ssize_t cbytes; uint32_t stack_image[32], stack_mask[32]; STRUCT_INIT(fbcursor, mode); if (ddi_copyin((void *)data, STRUCT_BUF(fbcursor), STRUCT_SIZE(fbcursor), mode)) return (EFAULT); set = STRUCT_FGET(fbcursor, set); /* Compute cursor bitmap bytes */ cbytes = STRUCT_FGET(fbcursor, size.y) * sizeof (softc->cur.image[0]); if (set & FB_CUR_SETSHAPE) { if (STRUCT_FGET(fbcursor, size.x) > 32 || STRUCT_FGET(fbcursor, size.y) > 32) { return (EINVAL); } /* copy cursor image into softc */ if (STRUCT_FGETP(fbcursor, image) && ddi_copyin(STRUCT_FGETP(fbcursor, image), &stack_image, cbytes, mode)) return (EFAULT); if (STRUCT_FGETP(fbcursor, mask) && ddi_copyin(STRUCT_FGETP(fbcursor, mask), &stack_mask, cbytes, mode)) return (EFAULT); } mutex_enter(&softc->mutex); if (set & FB_CUR_SETCUR) softc->cur.enable = STRUCT_FGET(fbcursor, enable); if (set & FB_CUR_SETPOS) softc->cur.pos = STRUCT_FGET(fbcursor, pos); if (set & FB_CUR_SETHOT) softc->cur.hot = STRUCT_FGET(fbcursor, hot); /* update hardware */ cg6_setcurpos(softc); if (set & FB_CUR_SETSHAPE) { if (STRUCT_FGETP(fbcursor, image)) { bzero((caddr_t)softc->cur.image, sizeof (softc->cur.image)); bcopy((caddr_t)&stack_image, (caddr_t)softc->cur.image, cbytes); } if (STRUCT_FGETP(fbcursor, mask)) { bzero((caddr_t)softc->cur.mask, sizeof (softc->cur.mask)); bcopy((caddr_t)&stack_mask, (caddr_t)softc->cur.mask, cbytes); } /* load into hardware */ softc->cur.size = STRUCT_FGET(fbcursor, size); cg6_setcurshape(softc); } mutex_exit(&softc->mutex); /* load colormap */ if (set & FB_CUR_SETCMAP) { cursor_cmap = 1; cmd = FBIOPUTCMAP; STRUCT_SET_HANDLE(fbcmap, mode, STRUCT_FADDR(fbcursor, cmap)); goto cmap_ioctl; } } break; case FBIOGCURSOR: { ssize_t cbytes; uint32_t stack_image[32], stack_mask[32]; STRUCT_INIT(fbcursor, mode); if (ddi_copyin((void *)data, STRUCT_BUF(fbcursor), STRUCT_SIZE(fbcursor), mode)) return (EFAULT); mutex_enter(&softc->mutex); STRUCT_FSET(fbcursor, set, 0); STRUCT_FSET(fbcursor, enable, softc->cur.enable); STRUCT_FSET(fbcursor, pos, softc->cur.pos); STRUCT_FSET(fbcursor, hot, softc->cur.hot); STRUCT_FSET(fbcursor, size, softc->cur.size); STRUCT_FSET(fbcursor, cmap.index, 0); STRUCT_FSET(fbcursor, cmap.count, 2); /* compute cursor bitmap bytes */ cbytes = softc->cur.size.y * sizeof (softc->cur.image[0]); bcopy(softc->cur.image, &stack_image, cbytes); bcopy(softc->cur.mask, &stack_mask, cbytes); mutex_exit(&softc->mutex); if (ddi_copyout(STRUCT_BUF(fbcursor), (void *)data, STRUCT_SIZE(fbcursor), mode)) return (EFAULT); /* if image pointer is non-null copy both bitmaps */ if (STRUCT_FGETP(fbcursor, image)) { if (ddi_copyout(&stack_image, STRUCT_FGETP(fbcursor, image), cbytes, mode)) return (EFAULT); if (ddi_copyout(&stack_mask, STRUCT_FGETP(fbcursor, mask), cbytes, mode)) return (EFAULT); } /* if red pointer is non-null copy colormap */ if (STRUCT_FGETP(fbcursor, cmap.red)) { cursor_cmap = 1; cmd = FBIOGETCMAP; /* * XX64 The code used to do this: * * data = (int)&((struct fbcursor *)data)->cmap; * * However the cmap_ioctl handler doesn't look * at 'data' so this assignment doesn't do anything. * Instead we made it set the correct field in the * fbcurpos structure we were passed in from * userland. */ STRUCT_SET_HANDLE(fbcmap, mode, STRUCT_FADDR(fbcursor, cmap)); goto cmap_ioctl; } } break; case FBIOSCURPOS: { struct fbcurpos stack_curpos; /* cursor position */ if (ddi_copyin((caddr_t)data, (caddr_t)&stack_curpos, sizeof (struct fbcurpos), mode)) return (EFAULT); mutex_enter(&softc->mutex); bcopy((caddr_t)&stack_curpos, (caddr_t)&softc->cur.pos, sizeof (struct fbcurpos)); cg6_setcurpos(softc); mutex_exit(&softc->mutex); } break; case FBIOGCURPOS: { struct fbcurpos stack_curpos; /* cursor position */ mutex_enter(&softc->mutex); bcopy((caddr_t)&softc->cur.pos, (caddr_t)&stack_curpos, sizeof (struct fbcurpos)); mutex_exit(&softc->mutex); if (ddi_copyout((caddr_t)&stack_curpos, (caddr_t)data, sizeof (struct fbcurpos), mode)) return (EFAULT); } break; case FBIOGCURMAX: { static struct fbcurpos curmax = {32, 32}; if (ddi_copyout((caddr_t)&curmax, (caddr_t)data, sizeof (struct fbcurpos), mode)) return (EFAULT); } break; #if CG6DEBUG >= 3 case 255: cg6_debug = (int)data; if (cg6_debug == -1) cg6_debug = CG6DEBUG; cmn_err(CE_CONT, "cg6_debug is now %d\n", cg6_debug); break; #endif default: return (ENOTTY); } /* switch(cmd) */ return (0); } static uint_t cg6_intr(caddr_t arg) { struct cg6_softc *softc = (struct cg6_softc *)arg; volatile uint32_t *in; volatile uint32_t *out; volatile uint32_t tmp; DEBUGF(7, (CE_CONT, "cg6_intr: softc=%x, vrtflag=%x\n", softc, softc->vrtflag)); mutex_enter(&softc->mutex); mutex_enter(&softc->interlock); if (!cg6_int_pending(softc)) { if (softc->intr_flag) { softc->intr_flag = 0; } else { if (softc->intrstats) { KIOIP->intrs[KSTAT_INTR_SPURIOUS]++; } mutex_exit(&softc->interlock); mutex_exit(&softc->mutex); return (DDI_INTR_UNCLAIMED); /* nope, not mine */ } } if (!(cg6_update_pending(softc) || (softc)->vrtflag)) { /* TODO catch stray interrupts? */ cg6_int_disable_intr(softc); if (softc->intrstats) { KIOIP->intrs[KSTAT_INTR_HARD]++; } mutex_exit(&softc->interlock); mutex_exit(&softc->mutex); return (DDI_INTR_CLAIMED); } if (softc->vrtflag & CG6VRTCTR) { if (softc->vrtmaps == 0) { softc->vrtflag &= ~CG6VRTCTR; } else *softc->vrtpage += 1; } if (softc->vrtflag & CG6VRTIOCTL) { softc->vrtflag &= ~CG6VRTIOCTL; cv_broadcast(&softc->vrtsleep); } if (cg6_update_pending(softc)) { volatile struct cg6_cmap *cmap = S_CMAP(softc); LOOP_T count = softc->cmap_count; /* load cursor color map */ if (softc->omap_update) { in = &softc->omap_image.omap_int[0]; out = (uint32_t *)& cmap->omap; /* background color */ cmap->addr = 1 << 24; tmp = in[0]; *out = tmp; *out = tmp <<= 8; *out = tmp <<= 8; /* foreground color */ cmap->addr = 3 << 24; *out = tmp <<= 8; tmp = in[1]; *out = tmp; *out = tmp <<= 8; } /* load main color map */ if (count) { LOOP_T index = softc->cmap_index; in = &softc->cmap_image.cmap_int[0]; out = (uint32_t *)& cmap->cmap; /* count multiples of 4 RGB entries */ count = (count + (index & 3) + 3) >> 2; /* round index to 4 entry boundary */ index &= ~3; cmap->addr = index << 24; PTR_INCR(uint32_t *, in, index * 3); /* copy 4 bytes (4/3 RGB entries) per loop iteration */ count *= 3; PR_LOOPV(count, tmp = *in++; *out = tmp; *out = tmp <<= 8; *out = tmp <<= 8; *out = tmp <<= 8); softc->cmap_count = 0; } softc->omap_update = 0; } cg6_int_disable_intr(softc); if (softc->vrtflag) cg6_int_enable(softc); if (softc->intrstats) { KIOIP->intrs[KSTAT_INTR_HARD]++; } mutex_exit(&softc->interlock); mutex_exit(&softc->mutex); return (DDI_INTR_CLAIMED); } /* * Initialize a colormap: background = white, all others = black */ static void cg6_reset_cmap(volatile uchar_t *cmap, uint_t entries) { bzero((char *)cmap, entries * 3); cmap[0] = 255; cmap[1] = 255; cmap[2] = 255; } /* * Compute color map update parameters: starting index and count. * If count is already nonzero, adjust values as necessary. * Zero count argument indicates cursor color map update desired. */ static void cg6_update_cmap(struct cg6_softc *softc, uint_t index, uint_t count) { uint_t high, low; if (count == 0) { softc->omap_update = 1; return; } high = softc->cmap_count; if (high != 0) { high += (low = softc->cmap_index); if (index < low) softc->cmap_index = low = index; if (index + count > high) high = index + count; softc->cmap_count = high - low; } else { softc->cmap_index = index; softc->cmap_count = count; } } /* * Copy colormap entries between red, green, or blue array and * interspersed rgb array. * * count > 0 : copy count bytes from buf to rgb * count < 0 : copy -count bytes from rgb to buf */ static void cg6_cmap_bcopy(uchar_t *bufp, uchar_t *rgb, uint_t count) { LOOP_T rcount = count; if (--rcount >= 0) PR_LOOPVP(rcount, *rgb = *bufp++; rgb += 3); else { rcount = -rcount - 2; PR_LOOPVP(rcount, *bufp++ = *rgb; rgb += 3); } } /* * This routine restores the color map to it's post-attach time values. */ static void cg6_restore_prom_cmap(struct cg6_softc *softc, volatile uchar_t *cmap, uint_t entries) { volatile struct cg6_cmap *hwcmap = S_CMAP(softc); volatile uint32_t *in, *out, tmp; LOOP_T count; out = (uint32_t *)&hwcmap->cmap; in = (uint32_t *)cmap; if (entries != 0) { hwcmap->addr = 0; count = ((entries + 3) >> 2) * 3; PR_LOOPV(count, tmp = *in++; *out = tmp; *out = tmp <<= 8; *out = tmp <<= 8; *out = tmp <<= 8); } } /* * enable/disable/update HW cursor */ static void cg6_setcurpos(struct cg6_softc *softc) { volatile struct thc *thc = S_THC(softc); thc->l_thc_cursor = softc->cur.enable ? (((softc->cur.pos.x - softc->cur.hot.x) << 16) | ((softc->cur.pos.y - softc->cur.hot.y) & 0xffff)) : CG6_CURSOR_OFFPOS; } /* * load HW cursor bitmaps */ static void cg6_setcurshape(struct cg6_softc *softc) { uint_t tmp, edge = 0; volatile uint_t *image, *mask, *hw; volatile struct thc *thc = S_THC(softc); int i; /* compute right edge mask */ if (softc->cur.size.x) edge = (uint_t)~ 0 << (32 - softc->cur.size.x); image = softc->cur.image; mask = softc->cur.mask; hw = (uint_t *)&thc->l_thc_cursora00; for (i = 0; i < 32; i++) { hw[i] = (tmp = mask[i] & edge); hw[i + 32] = tmp & image[i]; } } static void cg6_reset(struct cg6_softc *softc) { volatile struct thc *thc = S_THC(softc); /* disable HW cursor */ thc->l_thc_cursor = CG6_CURSOR_OFFPOS; /* reinitialize TEC */ { volatile struct tec *tec = S_TEC(softc); tec->l_tec_mv = 0; tec->l_tec_clip = 0; tec->l_tec_vdc = 0; } /* reinitialize FBC config register */ { volatile uint_t *fhc = S_FHC(softc); uint_t rev, conf; rev = *fhc >> FHC_CONFIG_REV_SHIFT & FHC_CONFIG_REV_MASK; if (rev <= 4) { /* PROM knows how to deal with LSC and above */ /* rev == 0 : FBC 0 (not available to customers) */ /* rev == 1 : FBC 1 */ /* rev == 2 : FBC 2 */ /* rev == 3 : Toshiba (never built) */ /* rev == 4 : Standard Cell (not built yet) */ /* rev == 5 : LSC rev 2 (buggy) */ /* rev == 6 : LSC rev 3 */ conf = *fhc & FHC_CONFIG_RES_MASK | FHC_CONFIG_CPU_68020; #if FBC_REV0 /* FBC0: test window = 0, disable fast rops */ if (rev == 0) conf |= FHC_CONFIG_TEST | FHC_CONFIG_FROP_DISABLE; else #endif /* FBC_REV0 */ /* test window = 1K x 1K */ conf |= FHC_CONFIG_TEST | (10 + 1) << FHC_CONFIG_TESTX_SHIFT | (10 + 1) << FHC_CONFIG_TESTY_SHIFT; /* FBC[01]: disable destination cache */ if (rev <= 1) conf |= FHC_CONFIG_DST_DISABLE; *fhc = conf; } } /* reprogram DAC to enable HW cursor use */ { volatile struct cg6_cmap *cmap = S_CMAP(softc); /* command register */ cmap->addr = 6 << 24; /* turn on CR1:0, overlay enable */ cmap->ctrl = cmap->ctrl | (0x3 << 24); } } /* * This code is no longer used, since OBP proms now do all device * initialization. Nevertheless, it is instructive and I'm going to * keep it in as a comment, should anyone ever want to know how to * do minimal device initialization. Note the c++ style embedded * comments. * * cg6_init(softc) * struct cg6_softc *softc; * { * // Initialize DAC * { * register struct cg6_cmap *cmap = S_CMAP(softc); * register char *p; * * static char dacval[] = { * 4, 0xff, * 5, 0, * 6, 0x73, * 7, 0, * 0 * }; * * // initialize DAC * for (p = dacval; *p; p += 2) { * cmap->addr = p[0] << 24; * cmap->ctrl = p[1] << 24; * } * } * * // Initialize THC * { * register struct thc *thc = S_THC(softc); * int vidon; * * vidon = thc_get_video(thc); * thc->l_thc_hcmisc = THC_HCMISC_RESET | THC_HCMISC_INIT; * thc->l_thc_hcmisc = THC_HCMISC_INIT; * * thc->l_thc_hchs = 0x010009; * thc->l_thc_hchsdvs = 0x570000; * thc->l_thc_hchd = 0x15005d; * thc->l_thc_hcvs = 0x010005; * thc->l_thc_hcvd = 0x2403a8; * thc->l_thc_hcr = 0x00016b; * * thc->l_thc_hcmisc = THC_HCMISC_RESET | THC_HCMISC_INIT; * thc->l_thc_hcmisc = THC_HCMISC_INIT; * * if (vidon) * thc_set_video(thc, _ONE_); * * DEBUGF(1, (CE_CONT, "TEC rev %d\n", * thc->l_thc_hcmisc >> THC_HCMISC_REV_SHIFT & * THC_HCMISC_REV_MASK)); * } * * // * // Initialize FHC for 1152 X 900 screen * // * { * volatile uint_t *fhc = S_FHC(softc), rev; * * rev = *fhc >> FHC_CONFIG_REV_SHIFT & FHC_CONFIG_REV_MASK; * DEBUGF(1, (CE_CONT, "cg6_init: FBC rev %d\n", rev)); * * // * // FBC0: disable fast rops FBC[01]: disable destination cache * // * *fhc = FHC_CONFIG_1152 | * FHC_CONFIG_CPU_68020 | * FHC_CONFIG_TEST | * * #if FBC_REV0 * (rev == 0 ? FHC_CONFIG_FROP_DISABLE : 0) | * #endif * * (rev <= 1 ? FHC_CONFIG_DST_DISABLE : 0); * } * } */ /* * from here on down, is the lego segment driver. this virtualizes the * lego register file by associating a register save area with each * mapping of the lego device (each lego segment). only one of these * mappings is valid at any time; a page fault on one of the invalid * mappings saves off the current lego context, invalidates the current * valid mapping, restores the former register contents appropriate to * the faulting mapping, and then validates it. * * this implements a graphical context switch that is transparent to the user. * * the TEC and FBC contain the interesting context registers. * */ /* * Per-segment info: * Some, but not all, segments are part of a context. * Any segment that is a MAP_PRIVATE mapping to the TEC or FBC * will be part of a unique context. MAP_SHARED mappings are part * of the shared context and all such programs must arbitrate among * themselves to keep from stepping on each other's register settings. * Mappings to the framebuffer may or may not be part of a context, * depending on exact hardware type. */ #define CG6MAP_SHARED 0x02 /* shared context */ #define CG6MAP_VRT 0x04 /* vrt page */ #define CG6MAP_FBCTEC 0X08 /* mapping includes fbc and/or tec */ #define CG6MAP_FB 0X10 /* mapping includes framebuffer */ #define CG6MAP_CTX (CG6MAP_FBCTEC | CG6MAP_FB) /* needs context */ static struct cg6map_pvt * cg6_pvt_alloc(struct cg6_cntxt *ctx, uint_t type, offset_t off, size_t len, struct cg6_softc *softc) { struct cg6map_pvt *pvt; /* * create the private data portion of the devmap object */ pvt = kmem_zalloc(sizeof (struct cg6map_pvt), KM_SLEEP); pvt->type = type; pvt->offset = off; pvt->len = len; pvt->context = ctx; pvt->softc = softc; /* * Link this pvt into the list of associated pvt's for this * context */ pvt->next = ctx->pvt; ctx->pvt = pvt; return (pvt); } /* * This routine is called through the cb_ops table to handle * the creation of lego (cg6) segments. */ /*ARGSUSED*/ static int cg6_segmap(dev_t dev, off_t off, struct as *as, caddr_t *addrp, off_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cred) { struct cg6_softc *softc = getsoftc(getminor(dev)); int error; DEBUGF(3, (CE_CONT, "segmap: off=%x, len=%x\n", off, len)); mutex_enter(&softc->mutex); /* * check to see if this is a VRT page */ if (off == CG6_VADDR_VRT) { if (len != pagesize) { mutex_exit(&softc->mutex); DEBUGF(3, (CE_CONT, "rejecting because off=vrt and len=%x\n", len)) return (EINVAL); } if (softc->vrtmaps++ == 0) { if (softc->vrtpage == NULL) { softc->vrtpage = (int *)ddi_umem_alloc( pagesize, KM_SLEEP, (void **)&softc->vrtcookie); } *softc->vrtpage = 0; softc->vrtflag |= CG6VRTCTR; cg6_int_enable(softc); } } /* * use the devmap framework for setting up the user mapping. */ error = devmap_setup(dev, (offset_t)off, as, addrp, (size_t)len, prot, maxprot, flags, cred); mutex_exit(&softc->mutex); return (error); } /* ARGSUSED */ static int cg6map_map(devmap_cookie_t dhp, dev_t dev, uint_t flags, offset_t off, size_t len, void **pvtp) { struct cg6_softc *softc = getsoftc(getminor(dev)); struct cg6_cntxt *ctx = (struct cg6_cntxt *)NULL; struct cg6_cntxt *shared_ctx = &softc->shared_ctx; struct cg6map_pvt *pvt; uint_t maptype = 0; DEBUGF(3, (CE_CONT, "cg6map_map: off = %x, len = %x\n", (uint_t)off, (uint_t)len)); /* * LSC DFB BUG KLUDGE: DFB must always be mapped private on the buggy * (chip rev. 5) LSC chip. This is done to ensure that nobody ever * touches the framebuffer without the segment driver getting involved * to make sure the registers are idle. This involves taking a page * fault, invalidating all other process's mappings to the fb, (and * performing a context switch?) * * Under pixrects, which maps the chips and the FB all at once, the * entire mapping becomes a context. This won't hurt pixrects but * entails unnecessary context switching. Under other libraries such * as XGL, which maps the chips private and the FB shared, the FB * becomes part of the context. Programs which only map the FB will * also become contexts, but since they don't map the chips, there's * no context to switch. */ if (off + len > CG6_VADDR_FBC && off < CG6_VADDR_FBC + CG6_FBCTEC_SZ) maptype |= CG6MAP_FBCTEC; if (off + len > CG6_VADDR_COLOR && off < CG6_VADDR_COLOR + CG6_FB_SZ) maptype |= CG6MAP_FB; /* * we now support MAP_SHARED and MAP_PRIVATE: * * MAP_SHARED means you get the shared context which is the traditional * mapping method. * * MAP_PRIVATE means you get your very own LEGO context. * * Note that you can't get to here without asking for one or the other, * but not both. */ if (softc->chiprev == 5 && (maptype & CG6MAP_FB)) flags = (flags & ~MAP_TYPE) | MAP_PRIVATE; if (flags & MAP_SHARED) { /* shared mapping */ ctx = shared_ctx; ctx->flag = CG6MAP_CTX; } else { ctx = ctx_map_insert(softc, maptype); ctx->flag |= maptype; DEBUGF(2, (CE_CONT, "cg6map_map: ** MAP_PRIVATE **. ctx = %x\n", ctx)); } pvt = cg6_pvt_alloc(ctx, maptype, off, len, softc); pvt->dhp = dhp; *pvtp = pvt; devmap_set_ctx_timeout(dhp, cg6_ctxholdval); return (DDI_SUCCESS); } /* * An access has been made to a context other than the current one */ /* ARGSUSED */ static int cg6map_access(devmap_cookie_t dhp, void *pvt, offset_t offset, size_t len, uint_t type, uint_t rw) { return (devmap_do_ctxmgt(dhp, pvt, offset, len, type, rw, cg6map_contextmgt)); } /* * called by the devmap framework to perform context switching. */ /* ARGSUSED */ static int cg6map_contextmgt(devmap_cookie_t dhp, void *pvt, offset_t offset, size_t len, uint_t type, uint_t rw) { struct cg6map_pvt *p = (struct cg6map_pvt *)pvt; struct cg6map_pvt *pvts; struct cg6_softc *softc = p->softc; volatile struct fbc *fbc; int err = 0; ASSERT(pvt); mutex_enter(&softc->mutex); DEBUGF(6, (CE_CONT, "cg6map_contextmgt: pvt = %x, dhp = %x, \ curctx = %x, context = %x\n", p, dhp, softc->curctx, p->context)); /* * Do we need to switch contexts? */ if (softc->curctx != p->context) { fbc = S_FBC(softc); /* * If there's a current context, save it */ if (softc->curctx != (struct cg6_cntxt *)NULL) { /* * Set segdev for current context and all associated * handles to intercept references to their addresses */ ASSERT(softc->curctx->pvt); for (pvts = softc->curctx->pvt; pvts != NULL; pvts = pvts->next) { err = devmap_unload(pvts->dhp, pvts->offset, pvts->len); if (err) { mutex_exit(&softc->mutex); return (err); } } if (cg6_cntxsave(fbc, S_TEC(softc), softc->curctx) == 0) { DEBUGF(1, (CE_CONT, "cgsix: context save failed\n")); /* * At this point we have no current context. */ softc->curctx = NULL; mutex_exit(&softc->mutex); return (-1); } } /* * Idle the chips */ CG6DELAY(!(fbc->l_fbc_status & L_FBC_BUSY), CG6_FBC_WAIT); if (fbc->l_fbc_status & L_FBC_BUSY) { DEBUGF(1, (CE_CONT, "cgsix: idle_cg6: status = %x\n", fbc->l_fbc_status)); /* * At this point we have no current context. */ softc->curctx = NULL; mutex_exit(&softc->mutex); return (-1); } DEBUGF(4, (CE_CONT, "loading context %x\n", p->context)); if (p->context->flag & CG6MAP_FBCTEC) if (cg6_cntxrestore(fbc, S_TEC(softc), p->context) == 0) { DEBUGF(1, (CE_CONT, "cgsix: context restore failed\n")); /* * At this point we have no current context. */ softc->curctx = NULL; mutex_exit(&softc->mutex); return (-1); } /* * switch software "context" */ softc->curctx = p->context; } ASSERT(p->context->pvt); if ((type == DEVMAP_LOCK) || (type == DEVMAP_UNLOCK)) { if ((err = devmap_load(p->dhp, offset, len, type, rw)) != 0) { mutex_exit(&softc->mutex); return (err); } } else { if ((err = devmap_load(p->dhp, p->offset, p->len, type, rw)) != 0) { mutex_exit(&softc->mutex); return (err); } } mutex_exit(&softc->mutex); return (err); } /* ARGSUSED */ static void cg6map_unmap(devmap_cookie_t dhp, void *pvtp, offset_t off, size_t len, devmap_cookie_t new_dhp1, void **pvtp1, devmap_cookie_t new_dhp2, void **pvtp2) { struct cg6map_pvt *p = (struct cg6map_pvt *)pvtp; struct cg6_softc *softc = p->softc; struct cg6_cntxt *ctx = p->context; struct cg6map_pvt *ptmp; struct cg6map_pvt *ppvts; struct cg6_cntxt *shared_ctx = &softc->shared_ctx; size_t length; DEBUGF(3, (CE_CONT, "cg6map_unmap: pvt = %x, dhp = %x, \ off = %x, len = %x, dhp1 = %x, dhp2 = %x\n", pvtp, dhp, (uint_t)off, len, new_dhp1, new_dhp2)); mutex_enter(&softc->mutex); /* * We are unmapping at the end of the mapping, if * new_dhp1 is not NULL. */ if (new_dhp1 != NULL) { ptmp = cg6_pvt_alloc(ctx, p->type, p->offset, (off - p->offset), softc); ptmp->dhp = new_dhp1; *pvtp1 = ptmp; } /* * We are unmapping at the beginning of the mapping, if * new_dhp2 is not NULL. */ if (new_dhp2 != NULL) { length = p->len - len - (off - p->offset); ptmp = cg6_pvt_alloc(ctx, p->type, (off + len), length, softc); ptmp->dhp = new_dhp2; *pvtp2 = ptmp; } /* * Remove the original pvt data */ ppvts = NULL; for (ptmp = ctx->pvt; ptmp != NULL; ptmp = ptmp->next) { if (ptmp == pvtp) { if (ppvts == NULL) { ctx->pvt = ptmp->next; } else { ppvts->next = ptmp->next; } kmem_free(pvtp, sizeof (struct cg6map_pvt)); break; } ppvts = ptmp; } /* * We want to remove the conext if both new_dhp1 and new_dhp2 are NULL. */ if (new_dhp1 == NULL && new_dhp2 == NULL) { /* * Remove the context if this is not the shared context * xand there are no more associated pvt's */ if ((ctx != shared_ctx) && (ctx->pvt == NULL)) { struct cg6_cntxt *ctxptr; if (ctx == softc->curctx) softc->curctx = NULL; /* * Scan private context list for entry to remove. * Check first to see if it's the head of our list. */ if (softc->pvt_ctx == ctx) { softc->pvt_ctx = ctx->link; kmem_free(ctx, sizeof (struct cg6_cntxt)); } else { for (ctxptr = softc->pvt_ctx; ctxptr != NULL; ctxptr = ctxptr->link) { if (ctxptr->link == ctx) { ctxptr->link = ctx->link; kmem_free(ctx, sizeof (struct cg6_cntxt)); } } } } /* * If the curctx is the shared context, and there are no * more pvt's for the shared context, set the curctx to * NULL to force a context switch on the next device access. */ if ((softc->curctx == shared_ctx) && (softc->curctx->pvt == NULL)) { softc->curctx = NULL; } } mutex_exit(&softc->mutex); } /* ARGSUSED */ static int cg6map_dup(devmap_cookie_t dhp, void *oldpvt, devmap_cookie_t new_dhp, void **newpvt) { struct cg6map_pvt *p = (struct cg6map_pvt *)oldpvt; struct cg6_softc *softc = p->softc; struct cg6map_pvt *pvt; struct cg6_cntxt *ctx; uint_t maptype; DEBUGF(3, (CE_CONT, "cg6map_dup: pvt=%x, dhp=%x, newdhp=%x\n", oldpvt, dhp, new_dhp)); mutex_enter(&softc->mutex); if (p->context != &softc->shared_ctx) { maptype = p->type; ctx = ctx_map_insert(softc, maptype); } else ctx = &softc->shared_ctx; pvt = cg6_pvt_alloc(ctx, p->type, p->offset, p->len, softc); pvt->dhp = new_dhp; *newpvt = pvt; if (p->context && (p->context->flag & CG6MAP_VRT)) { softc->vrtflag |= CG6VRTCTR; if (softc->vrtmaps == 0) cg6_int_enable(softc); softc->vrtmaps++; } mutex_exit(&softc->mutex); return (0); } /* * please don't mess with these defines... they may look like * a strange place for defines, but the context management code * wants them as they are. JMP * */ #undef L_TEC_VDC_INTRNL0 #define L_TEC_VDC_INTRNL0 0x8000 #undef L_TEC_VDC_INTRNL1 #define L_TEC_VDC_INTRNL1 0xa000 static int cg6_cntxsave(fbc, tec, saved) volatile struct fbc *fbc; volatile struct tec *tec; struct cg6_cntxt *saved; { int dreg; /* counts through the data registers */ uint_t *dp; /* points to a tec data register */ DEBUGF(5, (CE_CONT, "saving registers for %d\n", saved->pid)); CDELAY(!(fbc->l_fbc_status & L_FBC_BUSY), CG6_FBC_WAIT); if (fbc->l_fbc_status & L_FBC_BUSY) { DEBUGF(1, (CE_CONT, "cgsix: idle_cg6: status = %x\n", fbc->l_fbc_status)); return (0); } /* * start dumping stuff out. */ saved->fbc.status = fbc->l_fbc_status; saved->fbc.clipcheck = fbc->l_fbc_clipcheck; saved->fbc.misc = fbc->l_fbc_misc; saved->fbc.x0 = fbc->l_fbc_x0; saved->fbc.y0 = fbc->l_fbc_y0; saved->fbc.x1 = fbc->l_fbc_x1; saved->fbc.y1 = fbc->l_fbc_y1; saved->fbc.x2 = fbc->l_fbc_x2; saved->fbc.y2 = fbc->l_fbc_y2; saved->fbc.x3 = fbc->l_fbc_x3; saved->fbc.y3 = fbc->l_fbc_y3; saved->fbc.rasteroffx = fbc->l_fbc_rasteroffx; saved->fbc.rasteroffy = fbc->l_fbc_rasteroffy; saved->fbc.autoincx = fbc->l_fbc_autoincx; saved->fbc.autoincy = fbc->l_fbc_autoincy; saved->fbc.clipminx = fbc->l_fbc_clipminx; saved->fbc.clipminy = fbc->l_fbc_clipminy; saved->fbc.clipmaxx = fbc->l_fbc_clipmaxx; saved->fbc.clipmaxy = fbc->l_fbc_clipmaxy; saved->fbc.fcolor = fbc->l_fbc_fcolor; saved->fbc.bcolor = fbc->l_fbc_bcolor; saved->fbc.rasterop = fbc->l_fbc_rasterop; saved->fbc.planemask = fbc->l_fbc_planemask; saved->fbc.pixelmask = fbc->l_fbc_pixelmask; saved->fbc.pattalign = fbc->l_fbc_pattalign; saved->fbc.pattern0 = fbc->l_fbc_pattern0; saved->fbc.pattern1 = fbc->l_fbc_pattern1; saved->fbc.pattern2 = fbc->l_fbc_pattern2; saved->fbc.pattern3 = fbc->l_fbc_pattern3; saved->fbc.pattern4 = fbc->l_fbc_pattern4; saved->fbc.pattern5 = fbc->l_fbc_pattern5; saved->fbc.pattern6 = fbc->l_fbc_pattern6; saved->fbc.pattern7 = fbc->l_fbc_pattern7; /* * the tec matrix and clipping registers are easy. */ saved->tec.mv = tec->l_tec_mv; saved->tec.clip = tec->l_tec_clip; saved->tec.vdc = tec->l_tec_vdc; /* * the tec data registers are a little more non-obvious. * internally, they are 36 bits. what we see in the register * file is a 32-bit window onto the underlying data register. * changing the data-type in the VDC gets us either of two parts * of the data register. the internal format is opaque to us. */ tec->l_tec_vdc = (uint_t)L_TEC_VDC_INTRNL0; for (dreg = 0, dp = (uint_t *)&tec->l_tec_data00; dreg < 64; dreg++, dp++) { saved->tec.data[dreg][0] = *dp; } tec->l_tec_vdc = (uint_t)L_TEC_VDC_INTRNL1; for (dreg = 0, dp = (uint_t *)&tec->l_tec_data00; dreg < 64; dreg++, dp++) { saved->tec.data[dreg][1] = *dp; } return (1); } static int cg6_cntxrestore(fbc, tec, saved) volatile struct fbc *fbc; volatile struct tec *tec; struct cg6_cntxt *saved; { int dreg; uint_t *dp; DEBUGF(5, (CE_CONT, "restoring registers for %d\n", saved->pid)); /* * reload the tec data registers. see above for "how do they get * 36 bits in that itty-bitty int" */ tec->l_tec_vdc = (uint_t)L_TEC_VDC_INTRNL0; for (dreg = 0, dp = (uint_t *)&tec->l_tec_data00; dreg < 64; dreg++, dp++) { *dp = saved->tec.data[dreg][0]; } tec->l_tec_vdc = (uint_t)L_TEC_VDC_INTRNL1; for (dreg = 0, dp = (uint_t *)&tec->l_tec_data00; dreg < 64; dreg++, dp++) { *dp = saved->tec.data[dreg][1]; } /* * the tec matrix and clipping registers are next. */ tec->l_tec_mv = saved->tec.mv; tec->l_tec_clip = saved->tec.clip; tec->l_tec_vdc = saved->tec.vdc; /* * now the FBC vertex and address registers */ fbc->l_fbc_x0 = saved->fbc.x0; fbc->l_fbc_y0 = saved->fbc.y0; fbc->l_fbc_x1 = saved->fbc.x1; fbc->l_fbc_y1 = saved->fbc.y1; fbc->l_fbc_x2 = saved->fbc.x2; fbc->l_fbc_y2 = saved->fbc.y2; fbc->l_fbc_x3 = saved->fbc.x3; fbc->l_fbc_y3 = saved->fbc.y3; fbc->l_fbc_rasteroffx = saved->fbc.rasteroffx; fbc->l_fbc_rasteroffy = saved->fbc.rasteroffy; fbc->l_fbc_autoincx = saved->fbc.autoincx; fbc->l_fbc_autoincy = saved->fbc.autoincy; fbc->l_fbc_clipminx = saved->fbc.clipminx; fbc->l_fbc_clipminy = saved->fbc.clipminy; fbc->l_fbc_clipmaxx = saved->fbc.clipmaxx; fbc->l_fbc_clipmaxy = saved->fbc.clipmaxy; /* * restoring the attribute registers */ fbc->l_fbc_fcolor = saved->fbc.fcolor; fbc->l_fbc_bcolor = saved->fbc.bcolor; fbc->l_fbc_rasterop = saved->fbc.rasterop; fbc->l_fbc_planemask = saved->fbc.planemask; fbc->l_fbc_pixelmask = saved->fbc.pixelmask; fbc->l_fbc_pattalign = saved->fbc.pattalign; fbc->l_fbc_pattern0 = saved->fbc.pattern0; fbc->l_fbc_pattern1 = saved->fbc.pattern1; fbc->l_fbc_pattern2 = saved->fbc.pattern2; fbc->l_fbc_pattern3 = saved->fbc.pattern3; fbc->l_fbc_pattern4 = saved->fbc.pattern4; fbc->l_fbc_pattern5 = saved->fbc.pattern5; fbc->l_fbc_pattern6 = saved->fbc.pattern6; fbc->l_fbc_pattern7 = saved->fbc.pattern7; fbc->l_fbc_clipcheck = saved->fbc.clipcheck; fbc->l_fbc_misc = saved->fbc.misc; /* * lastly, let's restore the status */ fbc->l_fbc_status = saved->fbc.status; return (1); } /* * ctx_map_insert() * * Insert a mapping into the mapping list of a private context. First * determine if there's an existing context (e.g. one with the same PID * as the current one and that does not already have a mapping of this * type yet). If not, allocate a new one. Then insert mapping into this * context's list. * * The softc mutex must be held across calls to this routine. */ static struct cg6_cntxt * ctx_map_insert(struct cg6_softc *softc, int maptype) { struct cg6_cntxt *ctx; pid_t curpid = getpid(); DEBUGF(4, (CE_CONT, "ctx_map_insert: maptype=0x%x curpid=%d\n", maptype, curpid)); /* * If this is the first time we're here, then alloc space * for new context and depart. */ if (softc->pvt_ctx == NULL) { ctx = (struct cg6_cntxt *) kmem_zalloc(sizeof (struct cg6_cntxt), KM_SLEEP); ctx->pid = curpid; ctx->link = NULL; softc->pvt_ctx = ctx; return (ctx); } /* * Find existing context if one exists. We have a match if * we're the same process *and* there's not already a * mapping of this type assigned. */ for (ctx = softc->pvt_ctx; ctx != NULL; ctx = ctx->link) { if (ctx->pid == curpid && (maptype & ctx->flag & (CG6MAP_FBCTEC|CG6MAP_FB)) == 0) break; } /* no match, create a new one and add to softc list */ if (ctx == NULL) { ctx = (struct cg6_cntxt *) kmem_zalloc(sizeof (struct cg6_cntxt), KM_SLEEP); ctx->pid = curpid; ctx->link = softc->pvt_ctx; softc->pvt_ctx = ctx; } DEBUGF(4, (CE_CONT, "ctx_map_insert: returning ctx=0x%x\n", ctx)); return (ctx); } /* * getpid() * * Simple wrapper around process ID call to drv_getparm(9f). */ static pid_t getpid() { pid_t mypid; if (drv_getparm(PPID, &mypid) == -1) return (0); return (mypid); } #define DEVMEMORY 1 #define KERNELMEMORY 2 /*ARGSUSED*/ static int cg6_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len, size_t *maplen, uint_t model) { struct cg6_softc *softc = getsoftc(getminor(dev)); dev_info_t *dip = softc->devi; ssize_t diff; int err = 0; caddr_t kvaddr = NULL; ddi_umem_cookie_t cookie = NULL; offset_t offset = 0; uint_t rnumber = 0; uint_t type = DEVMEMORY; size_t length = len; uint_t map_type = 0; uint_t ctxmap = 0; struct devmap_callback_ctl *callbackops = &cg6map_ops; DEBUGF(2, (CE_CONT, "cg6_devmap(%d), off=0x%x, len=%x, dhp=%x\n", getminor(dev), (uint_t)off, len, dhp)); if ((diff = off - CG6_VADDR_COLOR) >= 0 && diff < softc->fbmappable) { if ((len + off) > (CG6_VADDR_COLOR + softc->fbmappable)) length = CG6_VADDR_COLOR + softc->fbmappable - off; offset = CG6_ADDR_COLOR + diff; map_type = CG6MAP_FB; } else if ((diff = off - CG6_VADDR_FBC) >= 0 && diff < CG6_FBCTEC_SZ) { if ((len + off) > (CG6_VADDR_FBC + CG6_FBCTEC_SZ)) length = (CG6_VADDR_FBC + CG6_FBCTEC_SZ) - off; offset = CG6_ADDR_FBC + diff; map_type = CG6MAP_FBCTEC; } else if ((diff = off - CG6_VADDR_CMAP) >= 0 && diff < CG6_CMAP_SZ) { if ((len + off) > (CG6_VADDR_CMAP + CG6_CMAP_SZ)) length = (CG6_VADDR_CMAP + CG6_CMAP_SZ) - off; offset = CG6_ADDR_CMAP + diff; } else if ((diff = off - CG6_VADDR_FHC) >= 0 && diff < CG6_FHCTHC_SZ) { if ((len + off) > (CG6_VADDR_FHC + CG6_FHCTHC_SZ)) length = (CG6_VADDR_FHC + CG6_FHCTHC_SZ) - off; offset = CG6_ADDR_FHC + diff; } else if ((diff = off - CG6_VADDR_ROM) >= 0 && diff < CG6_ROM_SZ) { if ((len + off) > (CG6_VADDR_ROM + CG6_ROM_SZ)) length = (CG6_VADDR_ROM + CG6_ROM_SZ) - off; offset = softc->addr_rom + diff; } else if ((diff = off - CG6_VADDR_DHC) >= 0 && diff < CG6_DHC_SZ) { if ((len + off) > (CG6_VADDR_DHC + CG6_DHC_SZ)) length = (CG6_VADDR_DHC + CG6_DHC_SZ) - off; offset = CG6_ADDR_DHC + diff; } else if ((diff = off - CG6_VADDR_ALT) >= 0 && diff < CG6_ALT_SZ) { if ((len + off) > (CG6_VADDR_ALT + CG6_ALT_SZ)) length = (CG6_VADDR_ALT + CG6_ALT_SZ) - off; offset = CG6_ADDR_ALT + diff; } else if ((diff = off - CG6_VADDR_VRT) >= 0 && diff < CG6_VRT_SZ) { if ((len + off) > (CG6_VADDR_VRT + CG6_VRT_SZ)) length = (CG6_VADDR_VRT + CG6_VRT_SZ) - off; type = KERNELMEMORY; if (softc->vrtpage != NULL) offset = diff; else kvaddr = (caddr_t)-1; cookie = softc->vrtcookie; } else if ((diff = off - CG3_MMAP_OFFSET) >= 0 && diff < softc->fbmappable) { if ((len + off) > (CG3_MMAP_OFFSET + softc->fbmappable)) length = CG3_MMAP_OFFSET + softc->fbmappable - off; offset = CG6_ADDR_COLOR + diff; } else if (off < CG6_VBASE) { if (softc->emulation == FBTYPE_SUN3COLOR) { if (off >= 0 && off < softc->fbmappable) { if ((len + off) > softc->fbmappable) length = softc->fbmappable - off; offset = CG6_ADDR_COLOR + diff; } else kvaddr = (caddr_t)-1; } else { /* softc->emulation == FBTYPE_SUN4COLOR */ if (off >= 0 && off < softc->dummysize) { if ((len + off) > softc->dummysize) length = softc->dummysize - off; offset = CG6_ADDR_COLOR + diff; } else if ((diff = off - softc->dummysize) < softc->fbmappable) { if ((len + off) > (softc->dummysize + softc->fbmappable)) length = softc->fbmappable - off; offset = CG6_ADDR_COLOR + diff; } } } else kvaddr = (caddr_t)-1; if (kvaddr == (caddr_t)-1) { DEBUGF(1, (CE_CONT, "cg6_devmap: no mapping off=0x%x, len=%x\n", (uint_t)off, len)); return (-1); } DEBUGF(2, (CE_CONT, "cg6_devmap: offset=0x%x, kvaddr=%x, length=%x\n", (uint_t)offset, kvaddr, length)); /* * LSC DFB BUG KLUDGE: DFB must always be mapped private on the buggy * (chip rev. 5) LSC chip. This is done to ensure that nobody ever * touches the framebuffer without the segment driver getting involved * to make sure the registers are idle. This involves taking a page * fault, invalidating all other process's mappings to the fb, (and * performing a context switch?) * * Under pixrects, which maps the chips and the FB all at once, the * entire mapping becomes a context. This won't hurt pixrects but * entails unnecessary context switching. Under other libraries such * as XGL, which maps the chips private and the FB shared, the FB * becomes part of the context. Programs which only map the FB will * also become contexts, but since they don't map the chips, there's * no context to switch. */ ctxmap = (softc->chiprev == 5) ? (CG6MAP_FBCTEC|CG6MAP_FB) : CG6MAP_FBCTEC; /* * do context switching on the TEC and FBC registers. */ if (map_type & ctxmap) callbackops = &cg6map_ops; else callbackops = NULL; if (type == DEVMEMORY) { if ((err = devmap_devmem_setup(dhp, dip, callbackops, rnumber, offset, length, PROT_ALL, DEVMAP_DEFAULTS, &endian_attr)) < 0) return (err); } else { if ((err = devmap_umem_setup(dhp, dip, callbackops, cookie, offset, length, PROT_ALL, DEVMAP_DEFAULTS, &endian_attr)) < 0) return (err); } *maplen = roundup(length, PAGESIZE); return (0); }