xref: /openbsd/sys/dev/fdt/simplefb.c (revision fc223b23)
1*fc223b23Sjsg /*	$OpenBSD: simplefb.c,v 1.9 2020/05/25 06:45:25 jsg 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 /*
466fd46d03Skettenis  * Supported pixel formats.  Layout ommitted when it matches the
476fd46d03Skettenis  * rasops defaults.
486fd46d03Skettenis  */
496fd46d03Skettenis 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 },
5762b18b30Skettenis 	{ "a8b8g8r8", 32 }
586fd46d03Skettenis };
596fd46d03Skettenis 
606fd46d03Skettenis struct simplefb_softc {
616fd46d03Skettenis 	struct device		sc_dev;
626fd46d03Skettenis 	bus_space_tag_t		sc_iot;
636fd46d03Skettenis 	bus_space_handle_t	sc_ioh;
646fd46d03Skettenis 
656fd46d03Skettenis 	struct rasops_info	sc_ri;
666fd46d03Skettenis 	struct wsscreen_descr	sc_wsd;
676fd46d03Skettenis 	struct wsscreen_list	sc_wsl;
686fd46d03Skettenis 	struct wsscreen_descr	*sc_scrlist[1];
696fd46d03Skettenis 
706fd46d03Skettenis 	struct simplefb_format	*sc_format;
716fd46d03Skettenis 	paddr_t			sc_paddr;
726fd46d03Skettenis 	psize_t			sc_psize;
736fd46d03Skettenis };
746fd46d03Skettenis 
7562b18b30Skettenis struct rasops_info simplefb_ri;
7662b18b30Skettenis struct wsscreen_descr simplefb_wsd = { "std" };
7762b18b30Skettenis struct wsdisplay_charcell simplefb_bs[SIMPLEFB_WIDTH * SIMPLEFB_HEIGHT];
7862b18b30Skettenis 
796fd46d03Skettenis int	simplefb_match(struct device *, void *, void *);
806fd46d03Skettenis void	simplefb_attach(struct device *, struct device *, void *);
816fd46d03Skettenis 
826fd46d03Skettenis struct cfattach simplefb_ca = {
836fd46d03Skettenis 	sizeof(struct simplefb_softc), simplefb_match, simplefb_attach
846fd46d03Skettenis };
856fd46d03Skettenis 
866fd46d03Skettenis struct cfdriver simplefb_cd = {
876fd46d03Skettenis 	NULL, "simplefb", DV_DULL
886fd46d03Skettenis };
896fd46d03Skettenis 
9062b18b30Skettenis const char *simplefb_init(int, struct rasops_info *);
9162b18b30Skettenis 
926fd46d03Skettenis int	simplefb_wsioctl(void *, u_long, caddr_t, int, struct proc *);
936fd46d03Skettenis paddr_t	simplefb_wsmmap(void *, off_t, int);
946fd46d03Skettenis int	simplefb_alloc_screen(void *, const struct wsscreen_descr *,
956fd46d03Skettenis 	    void **, int *, int *, long *);
966fd46d03Skettenis 
976fd46d03Skettenis struct wsdisplay_accessops simplefb_accessops = {
986fd46d03Skettenis 	.ioctl = simplefb_wsioctl,
996fd46d03Skettenis 	.mmap = simplefb_wsmmap,
1006fd46d03Skettenis 	.alloc_screen = simplefb_alloc_screen,
1016fd46d03Skettenis 	.free_screen = rasops_free_screen,
1026fd46d03Skettenis 	.show_screen = rasops_show_screen,
1036fd46d03Skettenis 	.getchar = rasops_getchar,
1046fd46d03Skettenis 	.load_font = rasops_load_font,
1056fd46d03Skettenis 	.list_font = rasops_list_font,
1066fd46d03Skettenis };
1076fd46d03Skettenis 
1086fd46d03Skettenis int
1096fd46d03Skettenis simplefb_match(struct device *parent, void *match, void *aux)
1106fd46d03Skettenis {
1116fd46d03Skettenis 	struct fdt_attach_args *faa = aux;
1126fd46d03Skettenis 
11367e2424dSpatrick 	/* Don't attach if it has no address space. */
11467e2424dSpatrick 	if (faa->fa_nreg < 1 || faa->fa_reg[0].size == 0)
11567e2424dSpatrick 		return 0;
11667e2424dSpatrick 
1172723123cSkettenis 	/* Don't attach if another driver already claimed our framebuffer. */
11867e2424dSpatrick 	if (rasops_check_framebuffer(faa->fa_reg[0].addr))
1192723123cSkettenis 		return 0;
1202723123cSkettenis 
1216fd46d03Skettenis 	return OF_is_compatible(faa->fa_node, "simple-framebuffer");
1226fd46d03Skettenis }
1236fd46d03Skettenis 
1246fd46d03Skettenis void
1256fd46d03Skettenis simplefb_attach(struct device *parent, struct device *self, void *aux)
1266fd46d03Skettenis {
1276fd46d03Skettenis 	struct simplefb_softc *sc = (struct simplefb_softc *)self;
1286fd46d03Skettenis 	struct fdt_attach_args *faa = aux;
1296fd46d03Skettenis 	struct rasops_info *ri = &sc->sc_ri;
1306fd46d03Skettenis 	struct wsemuldisplaydev_attach_args waa;
13162b18b30Skettenis 	const char *format;
13262b18b30Skettenis 	int console = 0;
13362b18b30Skettenis 	long defattr;
1346fd46d03Skettenis 
13562b18b30Skettenis 	format = simplefb_init(faa->fa_node, ri);
13662b18b30Skettenis 	if (format) {
1376fd46d03Skettenis 		printf(": unsupported format \"%s\"\n", format);
1386fd46d03Skettenis 		return;
1396fd46d03Skettenis 	}
1406fd46d03Skettenis 
14162b18b30Skettenis 	if (faa->fa_node == stdout_node)
14262b18b30Skettenis 		console = 1;
14362b18b30Skettenis 
1446fd46d03Skettenis 	sc->sc_iot = faa->fa_iot;
1456fd46d03Skettenis 	sc->sc_paddr = faa->fa_reg[0].addr;
1466fd46d03Skettenis 	sc->sc_psize = faa->fa_reg[0].size;
1476fd46d03Skettenis 	if (bus_space_map(sc->sc_iot, sc->sc_paddr, sc->sc_psize,
14862b18b30Skettenis 	    BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &sc->sc_ioh)) {
14962b18b30Skettenis 		printf(": can't map framebuffer\n");
15062b18b30Skettenis 		return;
15162b18b30Skettenis 	}
1526fd46d03Skettenis 
1536fd46d03Skettenis 	ri->ri_bits = bus_space_vaddr(sc->sc_iot, sc->sc_ioh);
1546fd46d03Skettenis 	ri->ri_hw = sc;
1556fd46d03Skettenis 
15662b18b30Skettenis 	if (console) {
15762b18b30Skettenis 		/* Preserve contents. */
15862b18b30Skettenis 		ri->ri_bs = simplefb_bs;
15962b18b30Skettenis 		ri->ri_flg &= ~RI_CLEAR;
16062b18b30Skettenis 	}
16162b18b30Skettenis 
162f3d31b3eSfcambus 	printf(": %dx%d, %dbpp\n", ri->ri_width, ri->ri_height, ri->ri_depth);
1636fd46d03Skettenis 
16462b18b30Skettenis 	ri->ri_flg |= RI_VCONS;
16562b18b30Skettenis 	rasops_init(ri, SIMPLEFB_HEIGHT, SIMPLEFB_WIDTH);
1666fd46d03Skettenis 
1676fd46d03Skettenis 	strlcpy(sc->sc_wsd.name, "std", sizeof(sc->sc_wsd.name));
1686fd46d03Skettenis 	sc->sc_wsd.capabilities = ri->ri_caps;
1696fd46d03Skettenis 	sc->sc_wsd.nrows = ri->ri_rows;
1706fd46d03Skettenis 	sc->sc_wsd.ncols = ri->ri_cols;
1716fd46d03Skettenis 	sc->sc_wsd.textops = &ri->ri_ops;
1726fd46d03Skettenis 	sc->sc_wsd.fontwidth = ri->ri_font->fontwidth;
1736fd46d03Skettenis 	sc->sc_wsd.fontheight = ri->ri_font->fontheight;
1746fd46d03Skettenis 
1756fd46d03Skettenis 	sc->sc_scrlist[0] = &sc->sc_wsd;
1766fd46d03Skettenis 	sc->sc_wsl.nscreens = 1;
1776fd46d03Skettenis 	sc->sc_wsl.screens = (const struct wsscreen_descr **)sc->sc_scrlist;
1786fd46d03Skettenis 
17962b18b30Skettenis 	if (console) {
180*fc223b23Sjsg 		ri->ri_ops.pack_attr(ri->ri_active, 0, 0, 0, &defattr);
18162b18b30Skettenis 		wsdisplay_cnattach(&sc->sc_wsd, ri->ri_active,
18262b18b30Skettenis 		    simplefb_ri.ri_ccol, simplefb_ri.ri_crow, defattr);
18362b18b30Skettenis 	}
18462b18b30Skettenis 
1856fd46d03Skettenis 	memset(&waa, 0, sizeof(waa));
1866fd46d03Skettenis 	waa.scrdata = &sc->sc_wsl;
1876fd46d03Skettenis 	waa.accessops = &simplefb_accessops;
1886fd46d03Skettenis 	waa.accesscookie = ri;
18962b18b30Skettenis 	waa.console = console;
1906fd46d03Skettenis 
1916fd46d03Skettenis 	config_found_sm(self, &waa, wsemuldisplaydevprint,
1926fd46d03Skettenis 	    wsemuldisplaydevsubmatch);
1936fd46d03Skettenis }
1946fd46d03Skettenis 
19562b18b30Skettenis const char *
19662b18b30Skettenis simplefb_init(int node, struct rasops_info *ri)
19762b18b30Skettenis {
19862b18b30Skettenis 	struct simplefb_format *fmt = NULL;
19962b18b30Skettenis 	static char format[16];
20062b18b30Skettenis 	int i;
20162b18b30Skettenis 
20262b18b30Skettenis 	format[0] = 0;
20362b18b30Skettenis 	OF_getprop(node, "format", format, sizeof(format));
20462b18b30Skettenis 	format[sizeof(format) - 1] = 0;
20562b18b30Skettenis 
20662b18b30Skettenis 	for (i = 0; i < nitems(simplefb_formats); i++) {
20762b18b30Skettenis 		if (strcmp(format, simplefb_formats[i].format) == 0) {
20862b18b30Skettenis 			fmt = &simplefb_formats[i];
20962b18b30Skettenis 			break;
21062b18b30Skettenis 		}
21162b18b30Skettenis 	}
21262b18b30Skettenis 	if (fmt == NULL)
21362b18b30Skettenis 		return format;
21462b18b30Skettenis 
21562b18b30Skettenis 	ri->ri_width = OF_getpropint(node, "width", 0);
21662b18b30Skettenis 	ri->ri_height = OF_getpropint(node, "height", 0);
21762b18b30Skettenis 	ri->ri_stride = OF_getpropint(node, "stride", 0);
21862b18b30Skettenis 	ri->ri_depth = fmt->depth;
21962b18b30Skettenis 	ri->ri_rpos = fmt->rpos;
22062b18b30Skettenis 	ri->ri_rnum = fmt->rnum;
22162b18b30Skettenis 	ri->ri_gpos = fmt->gpos;
22262b18b30Skettenis 	ri->ri_gnum = fmt->gnum;
22362b18b30Skettenis 	ri->ri_bpos = fmt->bpos;
22462b18b30Skettenis 	ri->ri_bnum = fmt->bnum;
22562b18b30Skettenis 	ri->ri_flg = RI_CENTER | RI_CLEAR | RI_FULLCLEAR | RI_WRONLY;
22662b18b30Skettenis 
22762b18b30Skettenis 	return NULL;
22862b18b30Skettenis }
22962b18b30Skettenis 
2306fd46d03Skettenis int
2316fd46d03Skettenis simplefb_wsioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
2326fd46d03Skettenis {
2336fd46d03Skettenis 	struct rasops_info *ri = v;
2348cd7757eSkettenis 	struct wsdisplay_param *dp = (struct wsdisplay_param *)data;
2356fd46d03Skettenis 	struct wsdisplay_fbinfo	*wdf;
2366fd46d03Skettenis 
2376fd46d03Skettenis 	switch (cmd) {
2388cd7757eSkettenis 	case WSDISPLAYIO_GETPARAM:
2398cd7757eSkettenis 		if (ws_get_param)
2408cd7757eSkettenis 			return ws_get_param(dp);
2418cd7757eSkettenis 		return -1;
2428cd7757eSkettenis 	case WSDISPLAYIO_SETPARAM:
2438cd7757eSkettenis 		if (ws_set_param)
2448cd7757eSkettenis 			return ws_set_param(dp);
2458cd7757eSkettenis 		return -1;
2466fd46d03Skettenis 	case WSDISPLAYIO_GTYPE:
2477321c2f6Sjsg 		*(u_int *)data = WSDISPLAY_TYPE_EFIFB;
2486fd46d03Skettenis 		return 0;
2496fd46d03Skettenis 	case WSDISPLAYIO_GINFO:
2506fd46d03Skettenis 		wdf = (struct wsdisplay_fbinfo *)data;
2516fd46d03Skettenis 		wdf->width = ri->ri_width;
2526fd46d03Skettenis 		wdf->height = ri->ri_height;
2536fd46d03Skettenis 		wdf->depth = ri->ri_depth;
2546fd46d03Skettenis 		wdf->cmsize = 0;	/* color map is unavailable */
2556fd46d03Skettenis 		break;
2566fd46d03Skettenis 	case WSDISPLAYIO_LINEBYTES:
2576fd46d03Skettenis 		*(u_int *)data = ri->ri_stride;
2586fd46d03Skettenis 		break;
2596fd46d03Skettenis 	case WSDISPLAYIO_SMODE:
2606fd46d03Skettenis 		break;
2616fd46d03Skettenis 	case WSDISPLAYIO_GETSUPPORTEDDEPTH:
2626fd46d03Skettenis 		switch (ri->ri_depth) {
2636fd46d03Skettenis 		case 32:
2646fd46d03Skettenis 			*(u_int *)data = WSDISPLAYIO_DEPTH_24_32;
2656fd46d03Skettenis 			break;
2666fd46d03Skettenis 		case 24:
2676fd46d03Skettenis 			*(u_int *)data = WSDISPLAYIO_DEPTH_24_24;
2686fd46d03Skettenis 			break;
2696fd46d03Skettenis 		case 16:
2706fd46d03Skettenis 			*(u_int *)data = WSDISPLAYIO_DEPTH_16;
2716fd46d03Skettenis 			break;
2726fd46d03Skettenis 		case 15:
2736fd46d03Skettenis 			*(u_int *)data = WSDISPLAYIO_DEPTH_15;
2746fd46d03Skettenis 			break;
2756fd46d03Skettenis 		default:
2766fd46d03Skettenis 			return -1;
2776fd46d03Skettenis 		}
2786fd46d03Skettenis 		break;
2796fd46d03Skettenis 	default:
2806fd46d03Skettenis 		return -1;
2816fd46d03Skettenis 	}
2826fd46d03Skettenis 
2836fd46d03Skettenis 	return 0;
2846fd46d03Skettenis }
2856fd46d03Skettenis 
2866fd46d03Skettenis paddr_t
2876fd46d03Skettenis simplefb_wsmmap(void *v, off_t off, int prot)
2886fd46d03Skettenis {
2896fd46d03Skettenis 	struct rasops_info *ri = v;
2906fd46d03Skettenis 	struct simplefb_softc *sc = ri->ri_hw;
2916fd46d03Skettenis 
2926fd46d03Skettenis 	if (off < 0 || off >= sc->sc_psize)
2936fd46d03Skettenis 		return -1;
2946fd46d03Skettenis 
2958186ecd8Skettenis 	return ((sc->sc_paddr + off) | PMAP_NOCACHE);
2966fd46d03Skettenis }
2976fd46d03Skettenis 
2986fd46d03Skettenis int
2996fd46d03Skettenis simplefb_alloc_screen(void *v, const struct wsscreen_descr *type,
3006fd46d03Skettenis     void **cookiep, int *curxp, int *curyp, long *attrp)
3016fd46d03Skettenis {
3026fd46d03Skettenis 	return rasops_alloc_screen(v, cookiep, curxp, curyp, attrp);
3036fd46d03Skettenis }
30462b18b30Skettenis 
30562b18b30Skettenis #include "ukbd.h"
30662b18b30Skettenis 
30762b18b30Skettenis #if NUKBD > 0
30862b18b30Skettenis #include <dev/usb/ukbdvar.h>
30962b18b30Skettenis #endif
31062b18b30Skettenis 
31162b18b30Skettenis void
31262b18b30Skettenis simplefb_init_cons(bus_space_tag_t iot)
31362b18b30Skettenis {
31462b18b30Skettenis 	struct rasops_info *ri = &simplefb_ri;
31562b18b30Skettenis 	bus_space_handle_t ioh;
31662b18b30Skettenis 	struct fdt_reg reg;
31762b18b30Skettenis 	void *node;
31862b18b30Skettenis 	long defattr = 0;
31962b18b30Skettenis 
32062b18b30Skettenis 	node = fdt_find_cons("simple-framebuffer");
32162b18b30Skettenis 	if (node == NULL)
32262b18b30Skettenis 		return;
32362b18b30Skettenis 
32462b18b30Skettenis 	if (fdt_get_reg(node, 0, &reg))
32562b18b30Skettenis 		return;
32662b18b30Skettenis 
32762b18b30Skettenis 	if (bus_space_map(iot, reg.addr, reg.size,
32862b18b30Skettenis 	    BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &ioh))
32962b18b30Skettenis 		return;
33062b18b30Skettenis 
33162b18b30Skettenis 	ri->ri_bits = bus_space_vaddr(iot, ioh);
33262b18b30Skettenis 
33362b18b30Skettenis 	if (simplefb_init(stdout_node, ri))
33462b18b30Skettenis 		return;
33562b18b30Skettenis 
33662b18b30Skettenis 	ri->ri_bs = simplefb_bs;
33762b18b30Skettenis 	rasops_init(ri, SIMPLEFB_HEIGHT, SIMPLEFB_WIDTH);
33862b18b30Skettenis 
33962b18b30Skettenis 	simplefb_wsd.capabilities = ri->ri_caps;
34062b18b30Skettenis 	simplefb_wsd.ncols = ri->ri_cols;
34162b18b30Skettenis 	simplefb_wsd.nrows = ri->ri_rows;
34262b18b30Skettenis 	simplefb_wsd.textops = &ri->ri_ops;
34362b18b30Skettenis 	simplefb_wsd.fontwidth = ri->ri_font->fontwidth;
34462b18b30Skettenis 	simplefb_wsd.fontheight = ri->ri_font->fontheight;
34562b18b30Skettenis 
346*fc223b23Sjsg 	ri->ri_ops.pack_attr(ri, 0, 0, 0, &defattr);
34762b18b30Skettenis 	wsdisplay_cnattach(&simplefb_wsd, ri, 0, 0, defattr);
34862b18b30Skettenis 
34962b18b30Skettenis #if NUKBD > 0
35062b18b30Skettenis 	/* Allow USB keyboards to become the console input device. */
35162b18b30Skettenis 	ukbd_cnattach();
35262b18b30Skettenis #endif
35362b18b30Skettenis }
354