/* $OpenBSD: simplefb.c,v 1.20 2023/04/16 11:34:32 kettenis Exp $ */ /* * Copyright (c) 2016 Mark Kettenis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #define SIMPLEFB_WIDTH 160 #define SIMPLEFB_HEIGHT 50 struct simplefb_format { const char *format; int depth; int rpos, rnum; int gpos, gnum; int bpos, bnum; }; /* * Supported pixel formats. Layout omitted when it matches the * rasops defaults. */ const struct simplefb_format simplefb_formats[] = { { "r5g6b5", 16 }, { "x1r5g5b5", 15 }, { "a1r5g5b5", 15 }, { "r8g8b8", 24 }, { "x8r8g8b8", 32, 16, 8, 8, 8, 0, 8 }, { "a8r8g8b8", 32, 16, 8, 8, 8, 0, 8 }, { "x8b8g8r8", 32 }, { "a8b8g8r8", 32 }, { "x2r10g10b10", 32, 20, 10, 10, 10, 0, 10 }, { "a2r10g10b10", 32, 20, 10, 10, 10, 0, 10 }, }; struct simplefb_softc { struct device sc_dev; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; struct rasops_info sc_ri; struct wsscreen_descr sc_wsd; struct wsscreen_list sc_wsl; struct wsscreen_descr *sc_scrlist[1]; struct simplefb_format *sc_format; paddr_t sc_paddr; psize_t sc_psize; }; void (*simplefb_burn_hook)(u_int) = NULL; struct rasops_info simplefb_ri; struct wsscreen_descr simplefb_wsd = { "std" }; struct wsdisplay_charcell simplefb_bs[SIMPLEFB_WIDTH * SIMPLEFB_HEIGHT]; int simplefb_match(struct device *, void *, void *); void simplefb_attach(struct device *, struct device *, void *); const struct cfattach simplefb_ca = { sizeof(struct simplefb_softc), simplefb_match, simplefb_attach }; struct cfdriver simplefb_cd = { NULL, "simplefb", DV_DULL }; const char *simplefb_init(int, struct rasops_info *); int simplefb_wsioctl(void *, u_long, caddr_t, int, struct proc *); paddr_t simplefb_wsmmap(void *, off_t, int); int simplefb_alloc_screen(void *, const struct wsscreen_descr *, void **, int *, int *, uint32_t *); void simplefb_burn_screen(void *, u_int, u_int); struct wsdisplay_accessops simplefb_accessops = { .ioctl = simplefb_wsioctl, .mmap = simplefb_wsmmap, .alloc_screen = simplefb_alloc_screen, .free_screen = rasops_free_screen, .show_screen = rasops_show_screen, .getchar = rasops_getchar, .load_font = rasops_load_font, .list_font = rasops_list_font, .scrollback = rasops_scrollback, .burn_screen = simplefb_burn_screen, }; int simplefb_match(struct device *parent, void *match, void *aux) { struct fdt_attach_args *faa = aux; /* Don't attach if it has no address space. */ if (faa->fa_nreg < 1 || faa->fa_reg[0].size == 0) return 0; /* Don't attach if another driver already claimed our framebuffer. */ if (rasops_check_framebuffer(faa->fa_reg[0].addr)) return 0; return OF_is_compatible(faa->fa_node, "simple-framebuffer"); } void simplefb_attach(struct device *parent, struct device *self, void *aux) { struct simplefb_softc *sc = (struct simplefb_softc *)self; struct fdt_attach_args *faa = aux; struct rasops_info *ri = &sc->sc_ri; struct wsemuldisplaydev_attach_args waa; const char *format; int console = 0; uint32_t defattr; format = simplefb_init(faa->fa_node, ri); if (format) { printf(": unsupported format \"%s\"\n", format); return; } if (faa->fa_node == stdout_node) console = 1; sc->sc_iot = faa->fa_iot; sc->sc_paddr = faa->fa_reg[0].addr; sc->sc_psize = faa->fa_reg[0].size; if (bus_space_map(sc->sc_iot, sc->sc_paddr, sc->sc_psize, BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &sc->sc_ioh)) { printf(": can't map framebuffer\n"); return; } ri->ri_bits = bus_space_vaddr(sc->sc_iot, sc->sc_ioh); ri->ri_hw = sc; if (console) { /* Preserve contents. */ ri->ri_bs = simplefb_bs; ri->ri_flg &= ~RI_CLEAR; } printf(": %dx%d, %dbpp\n", ri->ri_width, ri->ri_height, ri->ri_depth); ri->ri_flg |= RI_VCONS; rasops_init(ri, SIMPLEFB_HEIGHT, SIMPLEFB_WIDTH); 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; sc->sc_scrlist[0] = &sc->sc_wsd; sc->sc_wsl.nscreens = 1; sc->sc_wsl.screens = (const struct wsscreen_descr **)sc->sc_scrlist; if (console) { ri->ri_ops.pack_attr(ri->ri_active, 0, 0, 0, &defattr); wsdisplay_cnattach(&sc->sc_wsd, ri->ri_active, simplefb_ri.ri_ccol, simplefb_ri.ri_crow, defattr); } memset(&waa, 0, sizeof(waa)); waa.scrdata = &sc->sc_wsl; waa.accessops = &simplefb_accessops; waa.accesscookie = ri; waa.console = console; config_found_sm(self, &waa, wsemuldisplaydevprint, wsemuldisplaydevsubmatch); } const char * simplefb_init(int node, struct rasops_info *ri) { const struct simplefb_format *fmt = NULL; static char format[16]; int i; format[0] = 0; OF_getprop(node, "format", format, sizeof(format)); format[sizeof(format) - 1] = 0; for (i = 0; i < nitems(simplefb_formats); i++) { if (strcmp(format, simplefb_formats[i].format) == 0) { fmt = &simplefb_formats[i]; break; } } if (fmt == NULL) return format; ri->ri_width = OF_getpropint(node, "width", 0); ri->ri_height = OF_getpropint(node, "height", 0); ri->ri_stride = OF_getpropint(node, "stride", 0); ri->ri_depth = fmt->depth; ri->ri_rpos = fmt->rpos; ri->ri_rnum = fmt->rnum; ri->ri_gpos = fmt->gpos; ri->ri_gnum = fmt->gnum; ri->ri_bpos = fmt->bpos; ri->ri_bnum = fmt->bnum; ri->ri_flg = RI_CENTER | RI_CLEAR | RI_FULLCLEAR | RI_WRONLY; return NULL; } int simplefb_wsioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) { struct rasops_info *ri = v; struct simplefb_softc *sc = ri->ri_hw; struct wsdisplay_param *dp = (struct wsdisplay_param *)data; struct wsdisplay_fbinfo *wdf; switch (cmd) { case WSDISPLAYIO_GETPARAM: if (ws_get_param) return ws_get_param(dp); return -1; case WSDISPLAYIO_SETPARAM: if (ws_set_param) return ws_set_param(dp); return -1; case WSDISPLAYIO_GTYPE: *(u_int *)data = WSDISPLAY_TYPE_EFIFB; return 0; 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 = sc->sc_paddr & PAGE_MASK; wdf->cmsize = 0; /* color map is unavailable */ break; case WSDISPLAYIO_LINEBYTES: *(u_int *)data = ri->ri_stride; break; case WSDISPLAYIO_SMODE: break; case WSDISPLAYIO_GETSUPPORTEDDEPTH: switch (ri->ri_depth) { case 32: if (ri->ri_rnum == 10) *(u_int *)data = WSDISPLAYIO_DEPTH_30; else *(u_int *)data = WSDISPLAYIO_DEPTH_24_32; break; case 24: *(u_int *)data = WSDISPLAYIO_DEPTH_24_24; break; case 16: *(u_int *)data = WSDISPLAYIO_DEPTH_16; break; case 15: *(u_int *)data = WSDISPLAYIO_DEPTH_15; break; default: return -1; } break; case WSDISPLAYIO_GVIDEO: case WSDISPLAYIO_SVIDEO: break; default: return -1; } return 0; } paddr_t simplefb_wsmmap(void *v, off_t off, int prot) { struct rasops_info *ri = v; struct simplefb_softc *sc = ri->ri_hw; if (off < 0 || off >= (sc->sc_psize + (sc->sc_paddr & PAGE_MASK))) return -1; return (((sc->sc_paddr & ~PAGE_MASK) + off) | PMAP_NOCACHE); } int simplefb_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep, int *curxp, int *curyp, uint32_t *attrp) { return rasops_alloc_screen(v, cookiep, curxp, curyp, attrp); } void simplefb_burn_screen(void *v, u_int on, u_int flags) { if (simplefb_burn_hook != NULL) simplefb_burn_hook(on); } #include "ukbd.h" #if NUKBD > 0 #include #endif void simplefb_init_cons(bus_space_tag_t iot) { struct rasops_info *ri = &simplefb_ri; bus_space_handle_t ioh; struct fdt_reg reg; void *node; uint32_t defattr = 0; node = fdt_find_cons("simple-framebuffer"); if (node == NULL) return; if (fdt_get_reg(node, 0, ®)) return; if (bus_space_map(iot, reg.addr, reg.size, BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &ioh)) return; ri->ri_bits = bus_space_vaddr(iot, ioh); if (simplefb_init(stdout_node, ri)) return; ri->ri_bs = simplefb_bs; rasops_init(ri, SIMPLEFB_HEIGHT, SIMPLEFB_WIDTH); simplefb_wsd.capabilities = ri->ri_caps; simplefb_wsd.ncols = ri->ri_cols; simplefb_wsd.nrows = ri->ri_rows; simplefb_wsd.textops = &ri->ri_ops; simplefb_wsd.fontwidth = ri->ri_font->fontwidth; simplefb_wsd.fontheight = ri->ri_font->fontheight; ri->ri_ops.pack_attr(ri, 0, 0, 0, &defattr); wsdisplay_cnattach(&simplefb_wsd, ri, 0, 0, defattr); #if NUKBD > 0 /* Allow USB keyboards to become the console input device. */ ukbd_cnattach(); #endif }