xref: /openbsd-src/sys/dev/fdt/simplefb.c (revision c0dd97bfcad3dab6c31ec12b9de1274fd2d2f993)
1 /*	$OpenBSD: simplefb.c,v 1.2 2017/08/27 12:42:22 kettenis 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 <machine/bus.h>
23 #include <machine/fdt.h>
24 
25 #include <dev/ofw/openfirm.h>
26 #include <dev/ofw/fdt.h>
27 
28 #include <dev/wscons/wsconsio.h>
29 #include <dev/wscons/wsdisplayvar.h>
30 #include <dev/rasops/rasops.h>
31 
32 #define SIMPLEFB_WIDTH	160
33 #define SIMPLEFB_HEIGHT	50
34 
35 struct simplefb_format {
36 	const char *format;
37 	int depth;
38 	int rpos, rnum;
39 	int gpos, gnum;
40 	int bpos, bnum;
41 };
42 
43 /*
44  * Supported pixel formats.  Layout ommitted when it matches the
45  * rasops defaults.
46  */
47 struct simplefb_format simplefb_formats[] = {
48 	{ "r5g6b5", 16 },
49 	{ "x1r5g5b5", 15 },
50 	{ "a1r5g5b5", 15 },
51 	{ "r8g8b8", 24 },
52 	{ "x8r8g8b8", 32, 16, 8, 8, 8, 0, 8 },
53 	{ "a8r8g8b8", 32, 16, 8, 8, 8, 0, 8 },
54 	{ "x8b8g8r8", 32 },
55 	{ "a8b8g8r8", 32 }
56 };
57 
58 struct simplefb_softc {
59 	struct device		sc_dev;
60 	bus_space_tag_t		sc_iot;
61 	bus_space_handle_t	sc_ioh;
62 
63 	struct rasops_info	sc_ri;
64 	struct wsscreen_descr	sc_wsd;
65 	struct wsscreen_list	sc_wsl;
66 	struct wsscreen_descr	*sc_scrlist[1];
67 
68 	struct simplefb_format	*sc_format;
69 	paddr_t			sc_paddr;
70 	psize_t			sc_psize;
71 };
72 
73 struct rasops_info simplefb_ri;
74 struct wsscreen_descr simplefb_wsd = { "std" };
75 struct wsdisplay_charcell simplefb_bs[SIMPLEFB_WIDTH * SIMPLEFB_HEIGHT];
76 
77 int	simplefb_match(struct device *, void *, void *);
78 void	simplefb_attach(struct device *, struct device *, void *);
79 
80 struct cfattach simplefb_ca = {
81 	sizeof(struct simplefb_softc), simplefb_match, simplefb_attach
82 };
83 
84 struct cfdriver simplefb_cd = {
85 	NULL, "simplefb", DV_DULL
86 };
87 
88 const char *simplefb_init(int, struct rasops_info *);
89 
90 int	simplefb_wsioctl(void *, u_long, caddr_t, int, struct proc *);
91 paddr_t	simplefb_wsmmap(void *, off_t, int);
92 int	simplefb_alloc_screen(void *, const struct wsscreen_descr *,
93 	    void **, int *, int *, long *);
94 
95 struct wsdisplay_accessops simplefb_accessops = {
96 	.ioctl = simplefb_wsioctl,
97 	.mmap = simplefb_wsmmap,
98 	.alloc_screen = simplefb_alloc_screen,
99 	.free_screen = rasops_free_screen,
100 	.show_screen = rasops_show_screen,
101 	.getchar = rasops_getchar,
102 	.load_font = rasops_load_font,
103 	.list_font = rasops_list_font,
104 };
105 
106 int
107 simplefb_match(struct device *parent, void *match, void *aux)
108 {
109 	struct fdt_attach_args *faa = aux;
110 
111 	return OF_is_compatible(faa->fa_node, "simple-framebuffer");
112 }
113 
114 void
115 simplefb_attach(struct device *parent, struct device *self, void *aux)
116 {
117 	struct simplefb_softc *sc = (struct simplefb_softc *)self;
118 	struct fdt_attach_args *faa = aux;
119 	struct rasops_info *ri = &sc->sc_ri;
120 	struct wsemuldisplaydev_attach_args waa;
121 	const char *format;
122 	int console = 0;
123 	long defattr;
124 
125 	if (faa->fa_nreg < 1)
126 		return;
127 
128 	format = simplefb_init(faa->fa_node, ri);
129 	if (format) {
130 		printf(": unsupported format \"%s\"\n", format);
131 		return;
132 	}
133 
134 	if (faa->fa_node == stdout_node)
135 		console = 1;
136 
137 	sc->sc_iot = faa->fa_iot;
138 	sc->sc_paddr = faa->fa_reg[0].addr;
139 	sc->sc_psize = faa->fa_reg[0].size;
140 	if (bus_space_map(sc->sc_iot, sc->sc_paddr, sc->sc_psize,
141 	    BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &sc->sc_ioh)) {
142 		printf(": can't map framebuffer\n");
143 		return;
144 	}
145 
146 	ri->ri_bits = bus_space_vaddr(sc->sc_iot, sc->sc_ioh);
147 	ri->ri_hw = sc;
148 
149 	if (console) {
150 		/* Preserve contents. */
151 		ri->ri_bs = simplefb_bs;
152 		ri->ri_flg &= ~RI_CLEAR;
153 	}
154 
155 	printf(": %dx%d\n", ri->ri_width, ri->ri_height);
156 
157 	ri->ri_flg |= RI_VCONS;
158 	rasops_init(ri, SIMPLEFB_HEIGHT, SIMPLEFB_WIDTH);
159 
160 	strlcpy(sc->sc_wsd.name, "std", sizeof(sc->sc_wsd.name));
161 	sc->sc_wsd.capabilities = ri->ri_caps;
162 	sc->sc_wsd.nrows = ri->ri_rows;
163 	sc->sc_wsd.ncols = ri->ri_cols;
164 	sc->sc_wsd.textops = &ri->ri_ops;
165 	sc->sc_wsd.fontwidth = ri->ri_font->fontwidth;
166 	sc->sc_wsd.fontheight = ri->ri_font->fontheight;
167 
168 	sc->sc_scrlist[0] = &sc->sc_wsd;
169 	sc->sc_wsl.nscreens = 1;
170 	sc->sc_wsl.screens = (const struct wsscreen_descr **)sc->sc_scrlist;
171 
172 	if (console) {
173 		ri->ri_ops.alloc_attr(ri->ri_active, 0, 0, 0, &defattr);
174 		wsdisplay_cnattach(&sc->sc_wsd, ri->ri_active,
175 		    simplefb_ri.ri_ccol, simplefb_ri.ri_crow, defattr);
176 	}
177 
178 	memset(&waa, 0, sizeof(waa));
179 	waa.scrdata = &sc->sc_wsl;
180 	waa.accessops = &simplefb_accessops;
181 	waa.accesscookie = ri;
182 	waa.console = console;
183 
184 	config_found_sm(self, &waa, wsemuldisplaydevprint,
185 	    wsemuldisplaydevsubmatch);
186 }
187 
188 const char *
189 simplefb_init(int node, struct rasops_info *ri)
190 {
191 	struct simplefb_format *fmt = NULL;
192 	static char format[16];
193 	int i;
194 
195 	format[0] = 0;
196 	OF_getprop(node, "format", format, sizeof(format));
197 	format[sizeof(format) - 1] = 0;
198 
199 	for (i = 0; i < nitems(simplefb_formats); i++) {
200 		if (strcmp(format, simplefb_formats[i].format) == 0) {
201 			fmt = &simplefb_formats[i];
202 			break;
203 		}
204 	}
205 	if (fmt == NULL)
206 		return format;
207 
208 	ri->ri_width = OF_getpropint(node, "width", 0);
209 	ri->ri_height = OF_getpropint(node, "height", 0);
210 	ri->ri_stride = OF_getpropint(node, "stride", 0);
211 	ri->ri_depth = fmt->depth;
212 	ri->ri_rpos = fmt->rpos;
213 	ri->ri_rnum = fmt->rnum;
214 	ri->ri_gpos = fmt->gpos;
215 	ri->ri_gnum = fmt->gnum;
216 	ri->ri_bpos = fmt->bpos;
217 	ri->ri_bnum = fmt->bnum;
218 	ri->ri_flg = RI_CENTER | RI_CLEAR | RI_FULLCLEAR | RI_WRONLY;
219 
220 	return NULL;
221 }
222 
223 int
224 simplefb_wsioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
225 {
226 	struct rasops_info *ri = v;
227 	struct wsdisplay_fbinfo	*wdf;
228 
229 	switch (cmd) {
230 	case WSDISPLAYIO_GTYPE:
231 		*(int *)data = WSDISPLAY_TYPE_EFIFB;
232 		return 0;
233 	case WSDISPLAYIO_GINFO:
234 		wdf = (struct wsdisplay_fbinfo *)data;
235 		wdf->width = ri->ri_width;
236 		wdf->height = ri->ri_height;
237 		wdf->depth = ri->ri_depth;
238 		wdf->cmsize = 0;	/* color map is unavailable */
239 		break;
240 	case WSDISPLAYIO_LINEBYTES:
241 		*(u_int *)data = ri->ri_stride;
242 		break;
243 	case WSDISPLAYIO_SMODE:
244 		break;
245 	case WSDISPLAYIO_GETSUPPORTEDDEPTH:
246 		switch (ri->ri_depth) {
247 		case 32:
248 			*(u_int *)data = WSDISPLAYIO_DEPTH_24_32;
249 			break;
250 		case 24:
251 			*(u_int *)data = WSDISPLAYIO_DEPTH_24_24;
252 			break;
253 		case 16:
254 			*(u_int *)data = WSDISPLAYIO_DEPTH_16;
255 			break;
256 		case 15:
257 			*(u_int *)data = WSDISPLAYIO_DEPTH_15;
258 			break;
259 		default:
260 			return -1;
261 		}
262 		break;
263 	default:
264 		return -1;
265 	}
266 
267 	return 0;
268 }
269 
270 paddr_t
271 simplefb_wsmmap(void *v, off_t off, int prot)
272 {
273 	struct rasops_info *ri = v;
274 	struct simplefb_softc *sc = ri->ri_hw;
275 
276 	if (off < 0 || off >= sc->sc_psize)
277 		return -1;
278 
279 	return sc->sc_paddr + off;
280 }
281 
282 int
283 simplefb_alloc_screen(void *v, const struct wsscreen_descr *type,
284     void **cookiep, int *curxp, int *curyp, long *attrp)
285 {
286 	return rasops_alloc_screen(v, cookiep, curxp, curyp, attrp);
287 }
288 
289 #include "ukbd.h"
290 
291 #if NUKBD > 0
292 #include <dev/usb/ukbdvar.h>
293 #endif
294 
295 void
296 simplefb_init_cons(bus_space_tag_t iot)
297 {
298 	struct rasops_info *ri = &simplefb_ri;
299 	bus_space_handle_t ioh;
300 	struct fdt_reg reg;
301 	void *node;
302 	long defattr = 0;
303 
304 	node = fdt_find_cons("simple-framebuffer");
305 	if (node == NULL)
306 		return;
307 
308 	if (fdt_get_reg(node, 0, &reg))
309 		return;
310 
311 	if (bus_space_map(iot, reg.addr, reg.size,
312 	    BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &ioh))
313 		return;
314 
315 	ri->ri_bits = bus_space_vaddr(iot, ioh);
316 
317 	if (simplefb_init(stdout_node, ri))
318 		return;
319 
320 	ri->ri_bs = simplefb_bs;
321 	rasops_init(ri, SIMPLEFB_HEIGHT, SIMPLEFB_WIDTH);
322 
323 	simplefb_wsd.capabilities = ri->ri_caps;
324 	simplefb_wsd.ncols = ri->ri_cols;
325 	simplefb_wsd.nrows = ri->ri_rows;
326 	simplefb_wsd.textops = &ri->ri_ops;
327 	simplefb_wsd.fontwidth = ri->ri_font->fontwidth;
328 	simplefb_wsd.fontheight = ri->ri_font->fontheight;
329 
330 	ri->ri_ops.alloc_attr(ri, 0, 0, 0, &defattr);
331 	wsdisplay_cnattach(&simplefb_wsd, ri, 0, 0, defattr);
332 
333 #if NUKBD > 0
334 	/* Allow USB keyboards to become the console input device. */
335 	ukbd_cnattach();
336 #endif
337 }
338