xref: /openbsd-src/sys/dev/fdt/simplefb.c (revision afbb90bc0c46c1e38e61a28f8e9f58768dd7a98c)
1 /*	$OpenBSD: simplefb.c,v 1.21 2024/11/12 20:52:35 tobhe Exp $	*/
2 /*
3  * Copyright (c) 2016 Mark Kettenis
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 
22 #include <uvm/uvm_extern.h>
23 
24 #include <machine/bus.h>
25 #include <machine/fdt.h>
26 
27 #include <dev/ofw/openfirm.h>
28 #include <dev/ofw/fdt.h>
29 
30 #include <dev/wscons/wsconsio.h>
31 #include <dev/wscons/wsdisplayvar.h>
32 #include <dev/rasops/rasops.h>
33 
34 #define SIMPLEFB_WIDTH	160
35 #define SIMPLEFB_HEIGHT	50
36 
37 struct simplefb_format {
38 	const char *format;
39 	int depth;
40 	int rpos, rnum;
41 	int gpos, gnum;
42 	int bpos, bnum;
43 };
44 
45 /*
46  * Supported pixel formats.  Layout omitted when it matches the
47  * rasops defaults.
48  */
49 const struct simplefb_format simplefb_formats[] = {
50 	{ "r5g6b5", 16, 11, 5, 5, 6, 0, 5 },
51 	{ "x1r5g5b5", 15, 10, 5, 5, 5, 0, 5 },
52 	{ "a1r5g5b5", 15, 10, 5, 5, 5, 0, 5 },
53 	{ "r8g8b8", 24, 16, 8, 8, 8, 0, 8 },
54 	{ "x8r8g8b8", 32, 16, 8, 8, 8, 0, 8 },
55 	{ "a8r8g8b8", 32, 16, 8, 8, 8, 0, 8 },
56 	{ "x8b8g8r8", 32 },
57 	{ "a8b8g8r8", 32 },
58 	{ "x2r10g10b10", 32, 20, 10, 10, 10, 0, 10 },
59 	{ "a2r10g10b10", 32, 20, 10, 10, 10, 0, 10 },
60 };
61 
62 struct simplefb_softc {
63 	struct device		sc_dev;
64 	bus_space_tag_t		sc_iot;
65 	bus_space_handle_t	sc_ioh;
66 
67 	struct rasops_info	sc_ri;
68 	struct wsscreen_descr	sc_wsd;
69 	struct wsscreen_list	sc_wsl;
70 	struct wsscreen_descr	*sc_scrlist[1];
71 
72 	struct simplefb_format	*sc_format;
73 	paddr_t			sc_paddr;
74 	psize_t			sc_psize;
75 };
76 
77 void (*simplefb_burn_hook)(u_int) = NULL;
78 
79 struct rasops_info simplefb_ri;
80 struct wsscreen_descr simplefb_wsd = { "std" };
81 struct wsdisplay_charcell simplefb_bs[SIMPLEFB_WIDTH * SIMPLEFB_HEIGHT];
82 
83 int	simplefb_match(struct device *, void *, void *);
84 void	simplefb_attach(struct device *, struct device *, void *);
85 
86 const struct cfattach simplefb_ca = {
87 	sizeof(struct simplefb_softc), simplefb_match, simplefb_attach
88 };
89 
90 struct cfdriver simplefb_cd = {
91 	NULL, "simplefb", DV_DULL
92 };
93 
94 const char *simplefb_init(int, struct rasops_info *);
95 
96 int	simplefb_wsioctl(void *, u_long, caddr_t, int, struct proc *);
97 paddr_t	simplefb_wsmmap(void *, off_t, int);
98 int	simplefb_alloc_screen(void *, const struct wsscreen_descr *,
99 	    void **, int *, int *, uint32_t *);
100 void	simplefb_burn_screen(void *, u_int, u_int);
101 
102 struct wsdisplay_accessops simplefb_accessops = {
103 	.ioctl = simplefb_wsioctl,
104 	.mmap = simplefb_wsmmap,
105 	.alloc_screen = simplefb_alloc_screen,
106 	.free_screen = rasops_free_screen,
107 	.show_screen = rasops_show_screen,
108 	.getchar = rasops_getchar,
109 	.load_font = rasops_load_font,
110 	.list_font = rasops_list_font,
111 	.scrollback = rasops_scrollback,
112 	.burn_screen = simplefb_burn_screen,
113 };
114 
115 int
116 simplefb_match(struct device *parent, void *match, void *aux)
117 {
118 	struct fdt_attach_args *faa = aux;
119 
120 	/* Don't attach if it has no address space. */
121 	if (faa->fa_nreg < 1 || faa->fa_reg[0].size == 0)
122 		return 0;
123 
124 	/* Don't attach if another driver already claimed our framebuffer. */
125 	if (rasops_check_framebuffer(faa->fa_reg[0].addr))
126 		return 0;
127 
128 	return OF_is_compatible(faa->fa_node, "simple-framebuffer");
129 }
130 
131 void
132 simplefb_attach(struct device *parent, struct device *self, void *aux)
133 {
134 	struct simplefb_softc *sc = (struct simplefb_softc *)self;
135 	struct fdt_attach_args *faa = aux;
136 	struct rasops_info *ri = &sc->sc_ri;
137 	struct wsemuldisplaydev_attach_args waa;
138 	const char *format;
139 	int console = 0;
140 	uint32_t defattr;
141 
142 	format = simplefb_init(faa->fa_node, ri);
143 	if (format) {
144 		printf(": unsupported format \"%s\"\n", format);
145 		return;
146 	}
147 
148 	if (faa->fa_node == stdout_node)
149 		console = 1;
150 
151 	sc->sc_iot = faa->fa_iot;
152 	sc->sc_paddr = faa->fa_reg[0].addr;
153 	sc->sc_psize = faa->fa_reg[0].size;
154 	if (bus_space_map(sc->sc_iot, sc->sc_paddr, sc->sc_psize,
155 	    BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &sc->sc_ioh)) {
156 		printf(": can't map framebuffer\n");
157 		return;
158 	}
159 
160 	ri->ri_bits = bus_space_vaddr(sc->sc_iot, sc->sc_ioh);
161 	ri->ri_hw = sc;
162 
163 	if (console) {
164 		/* Preserve contents. */
165 		ri->ri_bs = simplefb_bs;
166 		ri->ri_flg &= ~RI_CLEAR;
167 	}
168 
169 	printf(": %dx%d, %dbpp\n", ri->ri_width, ri->ri_height, ri->ri_depth);
170 
171 	ri->ri_flg |= RI_VCONS;
172 	rasops_init(ri, SIMPLEFB_HEIGHT, SIMPLEFB_WIDTH);
173 
174 	strlcpy(sc->sc_wsd.name, "std", sizeof(sc->sc_wsd.name));
175 	sc->sc_wsd.capabilities = ri->ri_caps;
176 	sc->sc_wsd.nrows = ri->ri_rows;
177 	sc->sc_wsd.ncols = ri->ri_cols;
178 	sc->sc_wsd.textops = &ri->ri_ops;
179 	sc->sc_wsd.fontwidth = ri->ri_font->fontwidth;
180 	sc->sc_wsd.fontheight = ri->ri_font->fontheight;
181 
182 	sc->sc_scrlist[0] = &sc->sc_wsd;
183 	sc->sc_wsl.nscreens = 1;
184 	sc->sc_wsl.screens = (const struct wsscreen_descr **)sc->sc_scrlist;
185 
186 	if (console) {
187 		ri->ri_ops.pack_attr(ri->ri_active, 0, 0, 0, &defattr);
188 		wsdisplay_cnattach(&sc->sc_wsd, ri->ri_active,
189 		    simplefb_ri.ri_ccol, simplefb_ri.ri_crow, defattr);
190 	}
191 
192 	memset(&waa, 0, sizeof(waa));
193 	waa.scrdata = &sc->sc_wsl;
194 	waa.accessops = &simplefb_accessops;
195 	waa.accesscookie = ri;
196 	waa.console = console;
197 
198 	config_found_sm(self, &waa, wsemuldisplaydevprint,
199 	    wsemuldisplaydevsubmatch);
200 }
201 
202 const char *
203 simplefb_init(int node, struct rasops_info *ri)
204 {
205 	const struct simplefb_format *fmt = NULL;
206 	static char format[16];
207 	int i;
208 
209 	format[0] = 0;
210 	OF_getprop(node, "format", format, sizeof(format));
211 	format[sizeof(format) - 1] = 0;
212 
213 	for (i = 0; i < nitems(simplefb_formats); i++) {
214 		if (strcmp(format, simplefb_formats[i].format) == 0) {
215 			fmt = &simplefb_formats[i];
216 			break;
217 		}
218 	}
219 	if (fmt == NULL)
220 		return format;
221 
222 	ri->ri_width = OF_getpropint(node, "width", 0);
223 	ri->ri_height = OF_getpropint(node, "height", 0);
224 	ri->ri_stride = OF_getpropint(node, "stride", 0);
225 	ri->ri_depth = fmt->depth;
226 	ri->ri_rpos = fmt->rpos;
227 	ri->ri_rnum = fmt->rnum;
228 	ri->ri_gpos = fmt->gpos;
229 	ri->ri_gnum = fmt->gnum;
230 	ri->ri_bpos = fmt->bpos;
231 	ri->ri_bnum = fmt->bnum;
232 	ri->ri_flg = RI_CENTER | RI_CLEAR | RI_FULLCLEAR | RI_WRONLY;
233 
234 	return NULL;
235 }
236 
237 int
238 simplefb_wsioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
239 {
240 	struct rasops_info *ri = v;
241 	struct simplefb_softc *sc = ri->ri_hw;
242 	struct wsdisplay_param *dp = (struct wsdisplay_param *)data;
243 	struct wsdisplay_fbinfo	*wdf;
244 
245 	switch (cmd) {
246 	case WSDISPLAYIO_GETPARAM:
247 		if (ws_get_param)
248 			return ws_get_param(dp);
249 		return -1;
250 	case WSDISPLAYIO_SETPARAM:
251 		if (ws_set_param)
252 			return ws_set_param(dp);
253 		return -1;
254 	case WSDISPLAYIO_GTYPE:
255 		*(u_int *)data = WSDISPLAY_TYPE_EFIFB;
256 		return 0;
257 	case WSDISPLAYIO_GINFO:
258 		wdf = (struct wsdisplay_fbinfo *)data;
259 		wdf->width = ri->ri_width;
260 		wdf->height = ri->ri_height;
261 		wdf->depth = ri->ri_depth;
262 		wdf->stride = ri->ri_stride;
263 		wdf->offset = sc->sc_paddr & PAGE_MASK;
264 		wdf->cmsize = 0;	/* color map is unavailable */
265 		break;
266 	case WSDISPLAYIO_LINEBYTES:
267 		*(u_int *)data = ri->ri_stride;
268 		break;
269 	case WSDISPLAYIO_SMODE:
270 		break;
271 	case WSDISPLAYIO_GETSUPPORTEDDEPTH:
272 		switch (ri->ri_depth) {
273 		case 32:
274 			if (ri->ri_rnum == 10)
275 				*(u_int *)data = WSDISPLAYIO_DEPTH_30;
276 			else
277 				*(u_int *)data = WSDISPLAYIO_DEPTH_24_32;
278 			break;
279 		case 24:
280 			*(u_int *)data = WSDISPLAYIO_DEPTH_24_24;
281 			break;
282 		case 16:
283 			*(u_int *)data = WSDISPLAYIO_DEPTH_16;
284 			break;
285 		case 15:
286 			*(u_int *)data = WSDISPLAYIO_DEPTH_15;
287 			break;
288 		default:
289 			return -1;
290 		}
291 		break;
292 	case WSDISPLAYIO_GVIDEO:
293 	case WSDISPLAYIO_SVIDEO:
294 		break;
295 	default:
296 		return -1;
297 	}
298 
299 	return 0;
300 }
301 
302 paddr_t
303 simplefb_wsmmap(void *v, off_t off, int prot)
304 {
305 	struct rasops_info *ri = v;
306 	struct simplefb_softc *sc = ri->ri_hw;
307 
308 	if (off < 0 || off >= (sc->sc_psize + (sc->sc_paddr & PAGE_MASK)))
309 		return -1;
310 
311 	return (((sc->sc_paddr & ~PAGE_MASK) + off) | PMAP_NOCACHE);
312 }
313 
314 int
315 simplefb_alloc_screen(void *v, const struct wsscreen_descr *type,
316     void **cookiep, int *curxp, int *curyp, uint32_t *attrp)
317 {
318 	return rasops_alloc_screen(v, cookiep, curxp, curyp, attrp);
319 }
320 
321 void
322 simplefb_burn_screen(void *v, u_int on, u_int flags)
323 {
324 	if (simplefb_burn_hook != NULL)
325 		simplefb_burn_hook(on);
326 }
327 
328 #include "ukbd.h"
329 
330 #if NUKBD > 0
331 #include <dev/usb/ukbdvar.h>
332 #endif
333 
334 void
335 simplefb_init_cons(bus_space_tag_t iot)
336 {
337 	struct rasops_info *ri = &simplefb_ri;
338 	bus_space_handle_t ioh;
339 	struct fdt_reg reg;
340 	void *node;
341 	uint32_t defattr = 0;
342 
343 	node = fdt_find_cons("simple-framebuffer");
344 	if (node == NULL)
345 		return;
346 
347 	if (fdt_get_reg(node, 0, &reg))
348 		return;
349 
350 	if (bus_space_map(iot, reg.addr, reg.size,
351 	    BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &ioh))
352 		return;
353 
354 	ri->ri_bits = bus_space_vaddr(iot, ioh);
355 
356 	if (simplefb_init(stdout_node, ri))
357 		return;
358 
359 	ri->ri_bs = simplefb_bs;
360 	rasops_init(ri, SIMPLEFB_HEIGHT, SIMPLEFB_WIDTH);
361 
362 	simplefb_wsd.capabilities = ri->ri_caps;
363 	simplefb_wsd.ncols = ri->ri_cols;
364 	simplefb_wsd.nrows = ri->ri_rows;
365 	simplefb_wsd.textops = &ri->ri_ops;
366 	simplefb_wsd.fontwidth = ri->ri_font->fontwidth;
367 	simplefb_wsd.fontheight = ri->ri_font->fontheight;
368 
369 	ri->ri_ops.pack_attr(ri, 0, 0, 0, &defattr);
370 	wsdisplay_cnattach(&simplefb_wsd, ri, 0, 0, defattr);
371 
372 #if NUKBD > 0
373 	/* Allow USB keyboards to become the console input device. */
374 	ukbd_cnattach();
375 #endif
376 }
377