1*e0f76f79Skettenis /* $OpenBSD: simplefb.c,v 1.20 2023/04/16 11:34:32 kettenis Exp $ */
26fd46d03Skettenis /*
36fd46d03Skettenis * Copyright (c) 2016 Mark Kettenis
46fd46d03Skettenis *
56fd46d03Skettenis * Permission to use, copy, modify, and distribute this software for any
66fd46d03Skettenis * purpose with or without fee is hereby granted, provided that the above
76fd46d03Skettenis * copyright notice and this permission notice appear in all copies.
86fd46d03Skettenis *
96fd46d03Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
106fd46d03Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
116fd46d03Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
126fd46d03Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
136fd46d03Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
146fd46d03Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
156fd46d03Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
166fd46d03Skettenis */
176fd46d03Skettenis
186fd46d03Skettenis #include <sys/param.h>
196fd46d03Skettenis #include <sys/systm.h>
206fd46d03Skettenis #include <sys/device.h>
216fd46d03Skettenis
228186ecd8Skettenis #include <uvm/uvm_extern.h>
238186ecd8Skettenis
246fd46d03Skettenis #include <machine/bus.h>
256fd46d03Skettenis #include <machine/fdt.h>
266fd46d03Skettenis
276fd46d03Skettenis #include <dev/ofw/openfirm.h>
286fd46d03Skettenis #include <dev/ofw/fdt.h>
296fd46d03Skettenis
306fd46d03Skettenis #include <dev/wscons/wsconsio.h>
316fd46d03Skettenis #include <dev/wscons/wsdisplayvar.h>
326fd46d03Skettenis #include <dev/rasops/rasops.h>
336fd46d03Skettenis
3462b18b30Skettenis #define SIMPLEFB_WIDTH 160
3562b18b30Skettenis #define SIMPLEFB_HEIGHT 50
3662b18b30Skettenis
376fd46d03Skettenis struct simplefb_format {
386fd46d03Skettenis const char *format;
396fd46d03Skettenis int depth;
406fd46d03Skettenis int rpos, rnum;
416fd46d03Skettenis int gpos, gnum;
426fd46d03Skettenis int bpos, bnum;
436fd46d03Skettenis };
446fd46d03Skettenis
456fd46d03Skettenis /*
464b1a56afSjsg * Supported pixel formats. Layout omitted when it matches the
476fd46d03Skettenis * rasops defaults.
486fd46d03Skettenis */
49c39c0495Skn const struct simplefb_format simplefb_formats[] = {
506fd46d03Skettenis { "r5g6b5", 16 },
516fd46d03Skettenis { "x1r5g5b5", 15 },
526fd46d03Skettenis { "a1r5g5b5", 15 },
536fd46d03Skettenis { "r8g8b8", 24 },
5462b18b30Skettenis { "x8r8g8b8", 32, 16, 8, 8, 8, 0, 8 },
5562b18b30Skettenis { "a8r8g8b8", 32, 16, 8, 8, 8, 0, 8 },
5662b18b30Skettenis { "x8b8g8r8", 32 },
570af672a9Skettenis { "a8b8g8r8", 32 },
580af672a9Skettenis { "x2r10g10b10", 32, 20, 10, 10, 10, 0, 10 },
590af672a9Skettenis { "a2r10g10b10", 32, 20, 10, 10, 10, 0, 10 },
606fd46d03Skettenis };
616fd46d03Skettenis
626fd46d03Skettenis struct simplefb_softc {
636fd46d03Skettenis struct device sc_dev;
646fd46d03Skettenis bus_space_tag_t sc_iot;
656fd46d03Skettenis bus_space_handle_t sc_ioh;
666fd46d03Skettenis
676fd46d03Skettenis struct rasops_info sc_ri;
686fd46d03Skettenis struct wsscreen_descr sc_wsd;
696fd46d03Skettenis struct wsscreen_list sc_wsl;
706fd46d03Skettenis struct wsscreen_descr *sc_scrlist[1];
716fd46d03Skettenis
726fd46d03Skettenis struct simplefb_format *sc_format;
736fd46d03Skettenis paddr_t sc_paddr;
746fd46d03Skettenis psize_t sc_psize;
756fd46d03Skettenis };
766fd46d03Skettenis
77f2edca47Stobhe void (*simplefb_burn_hook)(u_int) = NULL;
78f2edca47Stobhe
7962b18b30Skettenis struct rasops_info simplefb_ri;
8062b18b30Skettenis struct wsscreen_descr simplefb_wsd = { "std" };
8162b18b30Skettenis struct wsdisplay_charcell simplefb_bs[SIMPLEFB_WIDTH * SIMPLEFB_HEIGHT];
8262b18b30Skettenis
836fd46d03Skettenis int simplefb_match(struct device *, void *, void *);
846fd46d03Skettenis void simplefb_attach(struct device *, struct device *, void *);
856fd46d03Skettenis
869fdf0c62Smpi const struct cfattach simplefb_ca = {
876fd46d03Skettenis sizeof(struct simplefb_softc), simplefb_match, simplefb_attach
886fd46d03Skettenis };
896fd46d03Skettenis
906fd46d03Skettenis struct cfdriver simplefb_cd = {
916fd46d03Skettenis NULL, "simplefb", DV_DULL
926fd46d03Skettenis };
936fd46d03Skettenis
9462b18b30Skettenis const char *simplefb_init(int, struct rasops_info *);
9562b18b30Skettenis
966fd46d03Skettenis int simplefb_wsioctl(void *, u_long, caddr_t, int, struct proc *);
976fd46d03Skettenis paddr_t simplefb_wsmmap(void *, off_t, int);
986fd46d03Skettenis int simplefb_alloc_screen(void *, const struct wsscreen_descr *,
99e0c3e559Sjsg void **, int *, int *, uint32_t *);
100f2edca47Stobhe void simplefb_burn_screen(void *, u_int, u_int);
1016fd46d03Skettenis
1026fd46d03Skettenis struct wsdisplay_accessops simplefb_accessops = {
1036fd46d03Skettenis .ioctl = simplefb_wsioctl,
1046fd46d03Skettenis .mmap = simplefb_wsmmap,
1056fd46d03Skettenis .alloc_screen = simplefb_alloc_screen,
1066fd46d03Skettenis .free_screen = rasops_free_screen,
1076fd46d03Skettenis .show_screen = rasops_show_screen,
1086fd46d03Skettenis .getchar = rasops_getchar,
1096fd46d03Skettenis .load_font = rasops_load_font,
1106fd46d03Skettenis .list_font = rasops_list_font,
111f2edca47Stobhe .scrollback = rasops_scrollback,
112f2edca47Stobhe .burn_screen = simplefb_burn_screen,
1136fd46d03Skettenis };
1146fd46d03Skettenis
1156fd46d03Skettenis int
simplefb_match(struct device * parent,void * match,void * aux)1166fd46d03Skettenis simplefb_match(struct device *parent, void *match, void *aux)
1176fd46d03Skettenis {
1186fd46d03Skettenis struct fdt_attach_args *faa = aux;
1196fd46d03Skettenis
12067e2424dSpatrick /* Don't attach if it has no address space. */
12167e2424dSpatrick if (faa->fa_nreg < 1 || faa->fa_reg[0].size == 0)
12267e2424dSpatrick return 0;
12367e2424dSpatrick
1242723123cSkettenis /* Don't attach if another driver already claimed our framebuffer. */
12567e2424dSpatrick if (rasops_check_framebuffer(faa->fa_reg[0].addr))
1262723123cSkettenis return 0;
1272723123cSkettenis
1286fd46d03Skettenis return OF_is_compatible(faa->fa_node, "simple-framebuffer");
1296fd46d03Skettenis }
1306fd46d03Skettenis
1316fd46d03Skettenis void
simplefb_attach(struct device * parent,struct device * self,void * aux)1326fd46d03Skettenis simplefb_attach(struct device *parent, struct device *self, void *aux)
1336fd46d03Skettenis {
1346fd46d03Skettenis struct simplefb_softc *sc = (struct simplefb_softc *)self;
1356fd46d03Skettenis struct fdt_attach_args *faa = aux;
1366fd46d03Skettenis struct rasops_info *ri = &sc->sc_ri;
1376fd46d03Skettenis struct wsemuldisplaydev_attach_args waa;
13862b18b30Skettenis const char *format;
13962b18b30Skettenis int console = 0;
140e0c3e559Sjsg uint32_t defattr;
1416fd46d03Skettenis
14262b18b30Skettenis format = simplefb_init(faa->fa_node, ri);
14362b18b30Skettenis if (format) {
1446fd46d03Skettenis printf(": unsupported format \"%s\"\n", format);
1456fd46d03Skettenis return;
1466fd46d03Skettenis }
1476fd46d03Skettenis
14862b18b30Skettenis if (faa->fa_node == stdout_node)
14962b18b30Skettenis console = 1;
15062b18b30Skettenis
1516fd46d03Skettenis sc->sc_iot = faa->fa_iot;
1526fd46d03Skettenis sc->sc_paddr = faa->fa_reg[0].addr;
1536fd46d03Skettenis sc->sc_psize = faa->fa_reg[0].size;
1546fd46d03Skettenis if (bus_space_map(sc->sc_iot, sc->sc_paddr, sc->sc_psize,
15562b18b30Skettenis BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &sc->sc_ioh)) {
15662b18b30Skettenis printf(": can't map framebuffer\n");
15762b18b30Skettenis return;
15862b18b30Skettenis }
1596fd46d03Skettenis
1606fd46d03Skettenis ri->ri_bits = bus_space_vaddr(sc->sc_iot, sc->sc_ioh);
1616fd46d03Skettenis ri->ri_hw = sc;
1626fd46d03Skettenis
16362b18b30Skettenis if (console) {
16462b18b30Skettenis /* Preserve contents. */
16562b18b30Skettenis ri->ri_bs = simplefb_bs;
16662b18b30Skettenis ri->ri_flg &= ~RI_CLEAR;
16762b18b30Skettenis }
16862b18b30Skettenis
169f3d31b3eSfcambus printf(": %dx%d, %dbpp\n", ri->ri_width, ri->ri_height, ri->ri_depth);
1706fd46d03Skettenis
17162b18b30Skettenis ri->ri_flg |= RI_VCONS;
17262b18b30Skettenis rasops_init(ri, SIMPLEFB_HEIGHT, SIMPLEFB_WIDTH);
1736fd46d03Skettenis
1746fd46d03Skettenis strlcpy(sc->sc_wsd.name, "std", sizeof(sc->sc_wsd.name));
1756fd46d03Skettenis sc->sc_wsd.capabilities = ri->ri_caps;
1766fd46d03Skettenis sc->sc_wsd.nrows = ri->ri_rows;
1776fd46d03Skettenis sc->sc_wsd.ncols = ri->ri_cols;
1786fd46d03Skettenis sc->sc_wsd.textops = &ri->ri_ops;
1796fd46d03Skettenis sc->sc_wsd.fontwidth = ri->ri_font->fontwidth;
1806fd46d03Skettenis sc->sc_wsd.fontheight = ri->ri_font->fontheight;
1816fd46d03Skettenis
1826fd46d03Skettenis sc->sc_scrlist[0] = &sc->sc_wsd;
1836fd46d03Skettenis sc->sc_wsl.nscreens = 1;
1846fd46d03Skettenis sc->sc_wsl.screens = (const struct wsscreen_descr **)sc->sc_scrlist;
1856fd46d03Skettenis
18662b18b30Skettenis if (console) {
187fc223b23Sjsg ri->ri_ops.pack_attr(ri->ri_active, 0, 0, 0, &defattr);
18862b18b30Skettenis wsdisplay_cnattach(&sc->sc_wsd, ri->ri_active,
18962b18b30Skettenis simplefb_ri.ri_ccol, simplefb_ri.ri_crow, defattr);
19062b18b30Skettenis }
19162b18b30Skettenis
1926fd46d03Skettenis memset(&waa, 0, sizeof(waa));
1936fd46d03Skettenis waa.scrdata = &sc->sc_wsl;
1946fd46d03Skettenis waa.accessops = &simplefb_accessops;
1956fd46d03Skettenis waa.accesscookie = ri;
19662b18b30Skettenis waa.console = console;
1976fd46d03Skettenis
1986fd46d03Skettenis config_found_sm(self, &waa, wsemuldisplaydevprint,
1996fd46d03Skettenis wsemuldisplaydevsubmatch);
2006fd46d03Skettenis }
2016fd46d03Skettenis
20262b18b30Skettenis const char *
simplefb_init(int node,struct rasops_info * ri)20362b18b30Skettenis simplefb_init(int node, struct rasops_info *ri)
20462b18b30Skettenis {
205c39c0495Skn const struct simplefb_format *fmt = NULL;
20662b18b30Skettenis static char format[16];
20762b18b30Skettenis int i;
20862b18b30Skettenis
20962b18b30Skettenis format[0] = 0;
21062b18b30Skettenis OF_getprop(node, "format", format, sizeof(format));
21162b18b30Skettenis format[sizeof(format) - 1] = 0;
21262b18b30Skettenis
21362b18b30Skettenis for (i = 0; i < nitems(simplefb_formats); i++) {
21462b18b30Skettenis if (strcmp(format, simplefb_formats[i].format) == 0) {
21562b18b30Skettenis fmt = &simplefb_formats[i];
21662b18b30Skettenis break;
21762b18b30Skettenis }
21862b18b30Skettenis }
21962b18b30Skettenis if (fmt == NULL)
22062b18b30Skettenis return format;
22162b18b30Skettenis
22262b18b30Skettenis ri->ri_width = OF_getpropint(node, "width", 0);
22362b18b30Skettenis ri->ri_height = OF_getpropint(node, "height", 0);
22462b18b30Skettenis ri->ri_stride = OF_getpropint(node, "stride", 0);
22562b18b30Skettenis ri->ri_depth = fmt->depth;
22662b18b30Skettenis ri->ri_rpos = fmt->rpos;
22762b18b30Skettenis ri->ri_rnum = fmt->rnum;
22862b18b30Skettenis ri->ri_gpos = fmt->gpos;
22962b18b30Skettenis ri->ri_gnum = fmt->gnum;
23062b18b30Skettenis ri->ri_bpos = fmt->bpos;
23162b18b30Skettenis ri->ri_bnum = fmt->bnum;
23262b18b30Skettenis ri->ri_flg = RI_CENTER | RI_CLEAR | RI_FULLCLEAR | RI_WRONLY;
23362b18b30Skettenis
23462b18b30Skettenis return NULL;
23562b18b30Skettenis }
23662b18b30Skettenis
2376fd46d03Skettenis int
simplefb_wsioctl(void * v,u_long cmd,caddr_t data,int flag,struct proc * p)2386fd46d03Skettenis simplefb_wsioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
2396fd46d03Skettenis {
2406fd46d03Skettenis struct rasops_info *ri = v;
24163294167Skettenis struct simplefb_softc *sc = ri->ri_hw;
2428cd7757eSkettenis struct wsdisplay_param *dp = (struct wsdisplay_param *)data;
2436fd46d03Skettenis struct wsdisplay_fbinfo *wdf;
2446fd46d03Skettenis
2456fd46d03Skettenis switch (cmd) {
2468cd7757eSkettenis case WSDISPLAYIO_GETPARAM:
2478cd7757eSkettenis if (ws_get_param)
2488cd7757eSkettenis return ws_get_param(dp);
2498cd7757eSkettenis return -1;
2508cd7757eSkettenis case WSDISPLAYIO_SETPARAM:
2518cd7757eSkettenis if (ws_set_param)
2528cd7757eSkettenis return ws_set_param(dp);
2538cd7757eSkettenis return -1;
2546fd46d03Skettenis case WSDISPLAYIO_GTYPE:
2557321c2f6Sjsg *(u_int *)data = WSDISPLAY_TYPE_EFIFB;
2566fd46d03Skettenis return 0;
2576fd46d03Skettenis case WSDISPLAYIO_GINFO:
2586fd46d03Skettenis wdf = (struct wsdisplay_fbinfo *)data;
2596fd46d03Skettenis wdf->width = ri->ri_width;
2606fd46d03Skettenis wdf->height = ri->ri_height;
2616fd46d03Skettenis wdf->depth = ri->ri_depth;
26263294167Skettenis wdf->stride = ri->ri_stride;
26363294167Skettenis wdf->offset = sc->sc_paddr & PAGE_MASK;
2646fd46d03Skettenis wdf->cmsize = 0; /* color map is unavailable */
2656fd46d03Skettenis break;
2666fd46d03Skettenis case WSDISPLAYIO_LINEBYTES:
2676fd46d03Skettenis *(u_int *)data = ri->ri_stride;
2686fd46d03Skettenis break;
2696fd46d03Skettenis case WSDISPLAYIO_SMODE:
2706fd46d03Skettenis break;
2716fd46d03Skettenis case WSDISPLAYIO_GETSUPPORTEDDEPTH:
2726fd46d03Skettenis switch (ri->ri_depth) {
2736fd46d03Skettenis case 32:
2745e550f18Skettenis if (ri->ri_rnum == 10)
2755e550f18Skettenis *(u_int *)data = WSDISPLAYIO_DEPTH_30;
2765e550f18Skettenis else
2776fd46d03Skettenis *(u_int *)data = WSDISPLAYIO_DEPTH_24_32;
2786fd46d03Skettenis break;
2796fd46d03Skettenis case 24:
2806fd46d03Skettenis *(u_int *)data = WSDISPLAYIO_DEPTH_24_24;
2816fd46d03Skettenis break;
2826fd46d03Skettenis case 16:
2836fd46d03Skettenis *(u_int *)data = WSDISPLAYIO_DEPTH_16;
2846fd46d03Skettenis break;
2856fd46d03Skettenis case 15:
2866fd46d03Skettenis *(u_int *)data = WSDISPLAYIO_DEPTH_15;
2876fd46d03Skettenis break;
2886fd46d03Skettenis default:
2896fd46d03Skettenis return -1;
2906fd46d03Skettenis }
2916fd46d03Skettenis break;
292*e0f76f79Skettenis case WSDISPLAYIO_GVIDEO:
293*e0f76f79Skettenis case WSDISPLAYIO_SVIDEO:
294*e0f76f79Skettenis break;
2956fd46d03Skettenis default:
2966fd46d03Skettenis return -1;
2976fd46d03Skettenis }
2986fd46d03Skettenis
2996fd46d03Skettenis return 0;
3006fd46d03Skettenis }
3016fd46d03Skettenis
3026fd46d03Skettenis paddr_t
simplefb_wsmmap(void * v,off_t off,int prot)3036fd46d03Skettenis simplefb_wsmmap(void *v, off_t off, int prot)
3046fd46d03Skettenis {
3056fd46d03Skettenis struct rasops_info *ri = v;
3066fd46d03Skettenis struct simplefb_softc *sc = ri->ri_hw;
3076fd46d03Skettenis
308ec0359a7Skettenis if (off < 0 || off >= (sc->sc_psize + (sc->sc_paddr & PAGE_MASK)))
3096fd46d03Skettenis return -1;
3106fd46d03Skettenis
311ec0359a7Skettenis return (((sc->sc_paddr & ~PAGE_MASK) + off) | PMAP_NOCACHE);
3126fd46d03Skettenis }
3136fd46d03Skettenis
3146fd46d03Skettenis int
simplefb_alloc_screen(void * v,const struct wsscreen_descr * type,void ** cookiep,int * curxp,int * curyp,uint32_t * attrp)3156fd46d03Skettenis simplefb_alloc_screen(void *v, const struct wsscreen_descr *type,
316e0c3e559Sjsg void **cookiep, int *curxp, int *curyp, uint32_t *attrp)
3176fd46d03Skettenis {
3186fd46d03Skettenis return rasops_alloc_screen(v, cookiep, curxp, curyp, attrp);
3196fd46d03Skettenis }
32062b18b30Skettenis
321f2edca47Stobhe void
simplefb_burn_screen(void * v,u_int on,u_int flags)322f2edca47Stobhe simplefb_burn_screen(void *v, u_int on, u_int flags)
323f2edca47Stobhe {
324f2edca47Stobhe if (simplefb_burn_hook != NULL)
325f2edca47Stobhe simplefb_burn_hook(on);
326f2edca47Stobhe }
327f2edca47Stobhe
32862b18b30Skettenis #include "ukbd.h"
32962b18b30Skettenis
33062b18b30Skettenis #if NUKBD > 0
33162b18b30Skettenis #include <dev/usb/ukbdvar.h>
33262b18b30Skettenis #endif
33362b18b30Skettenis
33462b18b30Skettenis void
simplefb_init_cons(bus_space_tag_t iot)33562b18b30Skettenis simplefb_init_cons(bus_space_tag_t iot)
33662b18b30Skettenis {
33762b18b30Skettenis struct rasops_info *ri = &simplefb_ri;
33862b18b30Skettenis bus_space_handle_t ioh;
33962b18b30Skettenis struct fdt_reg reg;
34062b18b30Skettenis void *node;
341e0c3e559Sjsg uint32_t defattr = 0;
34262b18b30Skettenis
34362b18b30Skettenis node = fdt_find_cons("simple-framebuffer");
34462b18b30Skettenis if (node == NULL)
34562b18b30Skettenis return;
34662b18b30Skettenis
34762b18b30Skettenis if (fdt_get_reg(node, 0, ®))
34862b18b30Skettenis return;
34962b18b30Skettenis
35062b18b30Skettenis if (bus_space_map(iot, reg.addr, reg.size,
35162b18b30Skettenis BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &ioh))
35262b18b30Skettenis return;
35362b18b30Skettenis
35462b18b30Skettenis ri->ri_bits = bus_space_vaddr(iot, ioh);
35562b18b30Skettenis
35662b18b30Skettenis if (simplefb_init(stdout_node, ri))
35762b18b30Skettenis return;
35862b18b30Skettenis
35962b18b30Skettenis ri->ri_bs = simplefb_bs;
36062b18b30Skettenis rasops_init(ri, SIMPLEFB_HEIGHT, SIMPLEFB_WIDTH);
36162b18b30Skettenis
36262b18b30Skettenis simplefb_wsd.capabilities = ri->ri_caps;
36362b18b30Skettenis simplefb_wsd.ncols = ri->ri_cols;
36462b18b30Skettenis simplefb_wsd.nrows = ri->ri_rows;
36562b18b30Skettenis simplefb_wsd.textops = &ri->ri_ops;
36662b18b30Skettenis simplefb_wsd.fontwidth = ri->ri_font->fontwidth;
36762b18b30Skettenis simplefb_wsd.fontheight = ri->ri_font->fontheight;
36862b18b30Skettenis
369fc223b23Sjsg ri->ri_ops.pack_attr(ri, 0, 0, 0, &defattr);
37062b18b30Skettenis wsdisplay_cnattach(&simplefb_wsd, ri, 0, 0, defattr);
37162b18b30Skettenis
37262b18b30Skettenis #if NUKBD > 0
37362b18b30Skettenis /* Allow USB keyboards to become the console input device. */
37462b18b30Skettenis ukbd_cnattach();
37562b18b30Skettenis #endif
37662b18b30Skettenis }
377