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, ®)) 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