jcs's openbsd hax
openbsd
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
37struct 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 */
49const 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
62struct 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
77void (*simplefb_burn_hook)(u_int) = NULL;
78
79struct rasops_info simplefb_ri;
80struct wsscreen_descr simplefb_wsd = { "std" };
81struct wsdisplay_charcell simplefb_bs[SIMPLEFB_WIDTH * SIMPLEFB_HEIGHT];
82
83int simplefb_match(struct device *, void *, void *);
84void simplefb_attach(struct device *, struct device *, void *);
85
86const struct cfattach simplefb_ca = {
87 sizeof(struct simplefb_softc), simplefb_match, simplefb_attach
88};
89
90struct cfdriver simplefb_cd = {
91 NULL, "simplefb", DV_DULL
92};
93
94const char *simplefb_init(int, struct rasops_info *);
95
96int simplefb_wsioctl(void *, u_long, caddr_t, int, struct proc *);
97paddr_t simplefb_wsmmap(void *, off_t, int);
98int simplefb_alloc_screen(void *, const struct wsscreen_descr *,
99 void **, int *, int *, uint32_t *);
100void simplefb_burn_screen(void *, u_int, u_int);
101
102struct 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
115int
116simplefb_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
131void
132simplefb_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
202const char *
203simplefb_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
237int
238simplefb_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
302paddr_t
303simplefb_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
314int
315simplefb_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
321void
322simplefb_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
334void
335simplefb_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, ®))
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}