/* $OpenBSD: vgafb.c,v 1.64 2022/12/31 05:06:18 gkoehler Exp $ */ /* $NetBSD: vga.c,v 1.3 1996/12/02 22:24:54 cgd Exp $ */ /* * Copyright (c) 1995, 1996 Carnegie-Mellon University. * All rights reserved. * * Author: Chris G. Demetriou * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include #include #include #include #include #include #include #include #include #include #include #include struct vgafb_softc { struct device sc_dev; int sc_node; bus_addr_t sc_mem_addr, sc_mmio_addr; bus_size_t sc_mem_size, sc_mmio_size; struct rasops_info sc_ri; uint8_t sc_cmap[256 * 3]; u_int sc_mode; struct wsscreen_descr sc_wsd; struct wsscreen_list sc_wsl; struct wsscreen_descr *sc_scrlist[1]; int sc_backlight_on; }; int vgafb_ioctl(void *, u_long, caddr_t, int, struct proc *); paddr_t vgafb_mmap(void *, off_t, int); int vgafb_alloc_screen(void *, const struct wsscreen_descr *, void **, int *, int *, uint32_t *); void vgafb_free_screen(void *, void *); int vgafb_show_screen(void *, void *, int, void (*cb)(void *, int, int), void *); int vgafb_load_font(void *, void *, struct wsdisplay_font *); int vgafb_list_font(void *, struct wsdisplay_font *); void vgafb_burn(void *v, u_int , u_int); void vgafb_restore_default_colors(struct vgafb_softc *); int vgafb_is_console(int); int vgafb_console_init(struct vgafb_softc *); int vgafb_mapregs(struct vgafb_softc *, struct pci_attach_args *); struct wsdisplay_accessops vgafb_accessops = { .ioctl = vgafb_ioctl, .mmap = vgafb_mmap, .alloc_screen = vgafb_alloc_screen, .free_screen = vgafb_free_screen, .show_screen = vgafb_show_screen, .load_font = vgafb_load_font, .list_font = vgafb_list_font, .burn_screen = vgafb_burn }; int vgafb_getcmap(uint8_t *, struct wsdisplay_cmap *); int vgafb_putcmap(uint8_t *, struct wsdisplay_cmap *); int vgafb_match(struct device *, void *, void *); void vgafb_attach(struct device *, struct device *, void *); const struct cfattach vgafb_ca = { sizeof(struct vgafb_softc), vgafb_match, vgafb_attach, }; struct cfdriver vgafb_cd = { NULL, "vgafb", DV_DULL, }; #ifdef APERTURE extern int allowaperture; #endif int vgafb_match(struct device *parent, void *match, void *aux) { struct pci_attach_args *pa = aux; int node; if (DEVICE_IS_VGA_PCI(pa->pa_class) == 0) { /* * XXX Graphic cards found in iMac G3 have a ``Misc'' * subclass, match them all. */ if (PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY || PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_DISPLAY_MISC) return (0); } /* * XXX Non-console devices do not get configured by the PROM, * XXX so do not attach them yet. */ node = PCITAG_NODE(pa->pa_tag); if (!vgafb_is_console(node)) return (0); return (1); } void vgafb_attach(struct device *parent, struct device *self, void *aux) { struct vgafb_softc *sc = (struct vgafb_softc *)self; struct pci_attach_args *pa = aux; struct wsemuldisplaydev_attach_args waa; sc->sc_node = PCITAG_NODE(pa->pa_tag); if (vgafb_mapregs(sc, pa)) return; if (vgafb_console_init(sc)) return; sc->sc_scrlist[0] = &sc->sc_wsd; sc->sc_wsl.nscreens = 1; sc->sc_wsl.screens = (const struct wsscreen_descr **)sc->sc_scrlist; waa.console = 1; waa.scrdata = &sc->sc_wsl; waa.accessops = &vgafb_accessops; waa.accesscookie = sc; waa.defaultscreens = 0; /* no need to keep the burner function if no hw support */ if (cons_backlight_available == 0) vgafb_accessops.burn_screen = NULL; else { sc->sc_backlight_on = WSDISPLAYIO_VIDEO_OFF; vgafb_burn(sc, WSDISPLAYIO_VIDEO_ON, 0); /* paranoia */ } #ifdef RAMDISK_HOOKS if (vga_aperture_needed(pa)) printf("%s: aperture needed\n", sc->sc_dev.dv_xname); #endif config_found(self, &waa, wsemuldisplaydevprint); } int vgafb_console_init(struct vgafb_softc *sc) { struct rasops_info *ri = &sc->sc_ri; uint32_t defattr; ri->ri_flg = RI_CENTER | RI_VCONS | RI_WRONLY; ri->ri_hw = sc; ofwconsswitch(ri); rasops_init(ri, 160, 160); strlcpy(sc->sc_wsd.name, "std", sizeof(sc->sc_wsd.name)); sc->sc_wsd.capabilities = ri->ri_caps; sc->sc_wsd.nrows = ri->ri_rows; sc->sc_wsd.ncols = ri->ri_cols; sc->sc_wsd.textops = &ri->ri_ops; sc->sc_wsd.fontwidth = ri->ri_font->fontwidth; sc->sc_wsd.fontheight = ri->ri_font->fontheight; ri->ri_ops.pack_attr(ri->ri_active, 0, 0, 0, &defattr); wsdisplay_cnattach(&sc->sc_wsd, ri->ri_active, ri->ri_ccol, ri->ri_crow, defattr); return (0); } void vgafb_restore_default_colors(struct vgafb_softc *sc) { bcopy(rasops_cmap, sc->sc_cmap, sizeof(sc->sc_cmap)); of_setcolors(sc->sc_cmap, 0, 256); } int vgafb_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) { struct vgafb_softc *sc = v; struct rasops_info *ri = &sc->sc_ri; struct wsdisplay_cmap *cm; struct wsdisplay_fbinfo *wdf; int rc; switch (cmd) { case WSDISPLAYIO_GTYPE: *(u_int *)data = WSDISPLAY_TYPE_PCIVGA; break; case WSDISPLAYIO_GINFO: wdf = (struct wsdisplay_fbinfo *)data; wdf->width = ri->ri_width; wdf->height = ri->ri_height; wdf->depth = ri->ri_depth; wdf->stride = ri->ri_stride; wdf->offset = 0; wdf->cmsize = 256; break; case WSDISPLAYIO_LINEBYTES: *(uint *)data = ri->ri_stride; break; case WSDISPLAYIO_GETCMAP: cm = (struct wsdisplay_cmap *)data; rc = vgafb_getcmap(sc->sc_cmap, cm); if (rc != 0) return rc; break; case WSDISPLAYIO_PUTCMAP: cm = (struct wsdisplay_cmap *)data; rc = vgafb_putcmap(sc->sc_cmap, cm); if (rc != 0) return (rc); if (ri->ri_depth == 8) of_setcolors(sc->sc_cmap, cm->index, cm->count); break; case WSDISPLAYIO_SMODE: sc->sc_mode = *(u_int *)data; if (ri->ri_depth == 8) vgafb_restore_default_colors(sc); break; case WSDISPLAYIO_GETPARAM: { struct wsdisplay_param *dp = (struct wsdisplay_param *)data; switch (dp->param) { case WSDISPLAYIO_PARAM_BRIGHTNESS: if (cons_backlight_available != 0) { dp->min = MIN_BRIGHTNESS; dp->max = MAX_BRIGHTNESS; dp->curval = cons_brightness; return 0; } return -1; case WSDISPLAYIO_PARAM_BACKLIGHT: if (cons_backlight_available != 0) { dp->min = 0; dp->max = 1; dp->curval = sc->sc_backlight_on; return 0; } else return -1; } } return -1; case WSDISPLAYIO_SETPARAM: { struct wsdisplay_param *dp = (struct wsdisplay_param *)data; switch (dp->param) { case WSDISPLAYIO_PARAM_BRIGHTNESS: if (cons_backlight_available == 1) { of_setbrightness(dp->curval); return 0; } else return -1; case WSDISPLAYIO_PARAM_BACKLIGHT: if (cons_backlight_available != 0) { vgafb_burn(sc, dp->curval ? WSDISPLAYIO_VIDEO_ON : WSDISPLAYIO_VIDEO_OFF, 0); return 0; } else return -1; } } return -1; case WSDISPLAYIO_SVIDEO: case WSDISPLAYIO_GVIDEO: break; case WSDISPLAYIO_GCURPOS: case WSDISPLAYIO_SCURPOS: case WSDISPLAYIO_GCURMAX: case WSDISPLAYIO_GCURSOR: case WSDISPLAYIO_SCURSOR: default: return -1; /* not supported yet */ } return (0); } paddr_t vgafb_mmap(void *v, off_t off, int prot) { struct vgafb_softc *sc = v; if (off & PGOFSET) return (-1); switch (sc->sc_mode) { case WSDISPLAYIO_MODE_MAPPED: #ifdef APERTURE if (allowaperture == 0) return (-1); #endif if (sc->sc_mmio_size == 0) return (-1); if (off >= sc->sc_mem_addr && off < (sc->sc_mem_addr + sc->sc_mem_size)) return (off); if (off >= sc->sc_mmio_addr && off < (sc->sc_mmio_addr + sc->sc_mmio_size)) return (off); break; case WSDISPLAYIO_MODE_DUMBFB: if (off >= 0x00000 && off < sc->sc_mem_size) return (sc->sc_mem_addr + off); break; } return (-1); } int vgafb_is_console(int node) { extern int fbnode; return (fbnode == node); } int vgafb_getcmap(uint8_t *cmap, struct wsdisplay_cmap *cm) { uint index = cm->index, count = cm->count, i; uint8_t ramp[256], *dst, *src; int rc; if (index >= 256 || count > 256 - index) return EINVAL; index *= 3; src = cmap + index; dst = ramp; for (i = 0; i < count; i++) *dst++ = *src, src += 3; rc = copyout(ramp, cm->red, count); if (rc != 0) return rc; src = cmap + index + 1; dst = ramp; for (i = 0; i < count; i++) *dst++ = *src, src += 3; rc = copyout(ramp, cm->green, count); if (rc != 0) return rc; src = cmap + index + 2; dst = ramp; for (i = 0; i < count; i++) *dst++ = *src, src += 3; rc = copyout(ramp, cm->blue, count); if (rc != 0) return rc; return 0; } int vgafb_putcmap(uint8_t *cmap, struct wsdisplay_cmap *cm) { uint index = cm->index, count = cm->count, i; uint8_t ramp[256], *dst, *src; int rc; if (index >= 256 || count > 256 - index) return EINVAL; index *= 3; rc = copyin(cm->red, ramp, count); if (rc != 0) return rc; dst = cmap + index; src = ramp; for (i = 0; i < count; i++) *dst = *src++, dst += 3; rc = copyin(cm->green, ramp, count); if (rc != 0) return rc; dst = cmap + index + 1; src = ramp; for (i = 0; i < count; i++) *dst = *src++, dst += 3; rc = copyin(cm->blue, ramp, count); if (rc != 0) return rc; dst = cmap + index + 2; src = ramp; for (i = 0; i < count; i++) *dst = *src++, dst += 3; return 0; } void vgafb_burn(void *v, u_int on, u_int flags) { struct vgafb_softc *sc = v; if (sc->sc_backlight_on != on) { of_setbacklight(on == WSDISPLAYIO_VIDEO_ON); sc->sc_backlight_on = on; } } int vgafb_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep, int *curxp, int *curyp, uint32_t *attrp) { struct vgafb_softc *sc = v; struct rasops_info *ri = &sc->sc_ri; return rasops_alloc_screen(ri, cookiep, curxp, curyp, attrp); } void vgafb_free_screen(void *v, void *cookie) { struct vgafb_softc *sc = v; struct rasops_info *ri = &sc->sc_ri; return rasops_free_screen(ri, cookie); } int vgafb_show_screen(void *v, void *cookie, int waitok, void (*cb)(void *, int, int), void *cbarg) { struct vgafb_softc *sc = v; struct rasops_info *ri = &sc->sc_ri; if (cookie == ri->ri_active) return (0); return rasops_show_screen(ri, cookie, waitok, cb, cbarg); } int vgafb_load_font(void *v, void *emulcookie, struct wsdisplay_font *font) { struct vgafb_softc *sc = v; struct rasops_info *ri = &sc->sc_ri; return rasops_load_font(ri, emulcookie, font); } int vgafb_list_font(void *v, struct wsdisplay_font *font) { struct vgafb_softc *sc = v; struct rasops_info *ri = &sc->sc_ri; return rasops_list_font(ri, font); } int vgafb_mapregs(struct vgafb_softc *sc, struct pci_attach_args *pa) { bus_addr_t ba; bus_size_t bs; int hasmem = 0, hasmmio = 0; uint32_t i, cf; int rv; /* * Look for the first 2 mem regions. For r128, this skips the * io region 0x14 and finds frame memory 0x10 and mmio 0x18. * For nvidia, this finds mmio 0x10 and frame memory 0x14. * Some nvidias have a 3rd mem region 0x18, which we ignore. */ for (i = PCI_MAPREG_START; i <= PCI_MAPREG_PPB_END; i += 4) { cf = pci_conf_read(pa->pa_pc, pa->pa_tag, i); if (PCI_MAPREG_TYPE(cf) == PCI_MAPREG_TYPE_MEM) { /* Memory mapping... frame memory or mmio? */ rv = pci_mem_find(pa->pa_pc, pa->pa_tag, i, &ba, &bs, NULL); if (rv != 0) continue; if (bs == 0 /* || ba == 0 */) { /* ignore this entry */ } else if (hasmem == 0) { /* * first memory slot found goes into memory, * this is for the case of no mmio */ sc->sc_mem_addr = ba; sc->sc_mem_size = bs; hasmem = 1; } else { /* * Oh, we have a second `memory' * region, is this region the vga memory * or mmio, we guess that memory is * the larger of the two. */ if (sc->sc_mem_size >= bs) { /* this is the mmio */ sc->sc_mmio_addr = ba; sc->sc_mmio_size = bs; hasmmio = 1; } else { /* this is the memory */ sc->sc_mmio_addr = sc->sc_mem_addr; sc->sc_mmio_size = sc->sc_mem_size; sc->sc_mem_addr = ba; sc->sc_mem_size = bs; } /* Ignore any other mem region. */ break; } } } /* failure to initialize io ports should not prevent attachment */ if (hasmem == 0) { printf(": could not find memory space\n"); return (1); } if (hasmmio) printf (", mmio"); printf("\n"); return (0); }