1 /* $OpenBSD: simplefb.c,v 1.16 2022/07/15 17:57:26 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 <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 struct simplefb_format simplefb_formats[] = { 50 { "r5g6b5", 16 }, 51 { "x1r5g5b5", 15 }, 52 { "a1r5g5b5", 15 }, 53 { "r8g8b8", 24 }, 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 struct rasops_info simplefb_ri; 78 struct wsscreen_descr simplefb_wsd = { "std" }; 79 struct wsdisplay_charcell simplefb_bs[SIMPLEFB_WIDTH * SIMPLEFB_HEIGHT]; 80 81 int simplefb_match(struct device *, void *, void *); 82 void simplefb_attach(struct device *, struct device *, void *); 83 84 const struct cfattach simplefb_ca = { 85 sizeof(struct simplefb_softc), simplefb_match, simplefb_attach 86 }; 87 88 struct cfdriver simplefb_cd = { 89 NULL, "simplefb", DV_DULL 90 }; 91 92 const char *simplefb_init(int, struct rasops_info *); 93 94 int simplefb_wsioctl(void *, u_long, caddr_t, int, struct proc *); 95 paddr_t simplefb_wsmmap(void *, off_t, int); 96 int simplefb_alloc_screen(void *, const struct wsscreen_descr *, 97 void **, int *, int *, uint32_t *); 98 99 struct wsdisplay_accessops simplefb_accessops = { 100 .ioctl = simplefb_wsioctl, 101 .mmap = simplefb_wsmmap, 102 .alloc_screen = simplefb_alloc_screen, 103 .free_screen = rasops_free_screen, 104 .show_screen = rasops_show_screen, 105 .getchar = rasops_getchar, 106 .load_font = rasops_load_font, 107 .list_font = rasops_list_font, 108 .scrollback = rasops_scrollback 109 }; 110 111 int 112 simplefb_match(struct device *parent, void *match, void *aux) 113 { 114 struct fdt_attach_args *faa = aux; 115 116 /* Don't attach if it has no address space. */ 117 if (faa->fa_nreg < 1 || faa->fa_reg[0].size == 0) 118 return 0; 119 120 /* Don't attach if another driver already claimed our framebuffer. */ 121 if (rasops_check_framebuffer(faa->fa_reg[0].addr)) 122 return 0; 123 124 return OF_is_compatible(faa->fa_node, "simple-framebuffer"); 125 } 126 127 void 128 simplefb_attach(struct device *parent, struct device *self, void *aux) 129 { 130 struct simplefb_softc *sc = (struct simplefb_softc *)self; 131 struct fdt_attach_args *faa = aux; 132 struct rasops_info *ri = &sc->sc_ri; 133 struct wsemuldisplaydev_attach_args waa; 134 const char *format; 135 int console = 0; 136 uint32_t defattr; 137 138 format = simplefb_init(faa->fa_node, ri); 139 if (format) { 140 printf(": unsupported format \"%s\"\n", format); 141 return; 142 } 143 144 if (faa->fa_node == stdout_node) 145 console = 1; 146 147 sc->sc_iot = faa->fa_iot; 148 sc->sc_paddr = faa->fa_reg[0].addr; 149 sc->sc_psize = faa->fa_reg[0].size; 150 if (bus_space_map(sc->sc_iot, sc->sc_paddr, sc->sc_psize, 151 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &sc->sc_ioh)) { 152 printf(": can't map framebuffer\n"); 153 return; 154 } 155 156 ri->ri_bits = bus_space_vaddr(sc->sc_iot, sc->sc_ioh); 157 ri->ri_hw = sc; 158 159 if (console) { 160 /* Preserve contents. */ 161 ri->ri_bs = simplefb_bs; 162 ri->ri_flg &= ~RI_CLEAR; 163 } 164 165 printf(": %dx%d, %dbpp\n", ri->ri_width, ri->ri_height, ri->ri_depth); 166 167 ri->ri_flg |= RI_VCONS; 168 rasops_init(ri, SIMPLEFB_HEIGHT, SIMPLEFB_WIDTH); 169 170 strlcpy(sc->sc_wsd.name, "std", sizeof(sc->sc_wsd.name)); 171 sc->sc_wsd.capabilities = ri->ri_caps; 172 sc->sc_wsd.nrows = ri->ri_rows; 173 sc->sc_wsd.ncols = ri->ri_cols; 174 sc->sc_wsd.textops = &ri->ri_ops; 175 sc->sc_wsd.fontwidth = ri->ri_font->fontwidth; 176 sc->sc_wsd.fontheight = ri->ri_font->fontheight; 177 178 sc->sc_scrlist[0] = &sc->sc_wsd; 179 sc->sc_wsl.nscreens = 1; 180 sc->sc_wsl.screens = (const struct wsscreen_descr **)sc->sc_scrlist; 181 182 if (console) { 183 ri->ri_ops.pack_attr(ri->ri_active, 0, 0, 0, &defattr); 184 wsdisplay_cnattach(&sc->sc_wsd, ri->ri_active, 185 simplefb_ri.ri_ccol, simplefb_ri.ri_crow, defattr); 186 } 187 188 memset(&waa, 0, sizeof(waa)); 189 waa.scrdata = &sc->sc_wsl; 190 waa.accessops = &simplefb_accessops; 191 waa.accesscookie = ri; 192 waa.console = console; 193 194 config_found_sm(self, &waa, wsemuldisplaydevprint, 195 wsemuldisplaydevsubmatch); 196 } 197 198 const char * 199 simplefb_init(int node, struct rasops_info *ri) 200 { 201 struct simplefb_format *fmt = NULL; 202 static char format[16]; 203 int i; 204 205 format[0] = 0; 206 OF_getprop(node, "format", format, sizeof(format)); 207 format[sizeof(format) - 1] = 0; 208 209 for (i = 0; i < nitems(simplefb_formats); i++) { 210 if (strcmp(format, simplefb_formats[i].format) == 0) { 211 fmt = &simplefb_formats[i]; 212 break; 213 } 214 } 215 if (fmt == NULL) 216 return format; 217 218 ri->ri_width = OF_getpropint(node, "width", 0); 219 ri->ri_height = OF_getpropint(node, "height", 0); 220 ri->ri_stride = OF_getpropint(node, "stride", 0); 221 ri->ri_depth = fmt->depth; 222 ri->ri_rpos = fmt->rpos; 223 ri->ri_rnum = fmt->rnum; 224 ri->ri_gpos = fmt->gpos; 225 ri->ri_gnum = fmt->gnum; 226 ri->ri_bpos = fmt->bpos; 227 ri->ri_bnum = fmt->bnum; 228 ri->ri_flg = RI_CENTER | RI_CLEAR | RI_FULLCLEAR | RI_WRONLY; 229 230 return NULL; 231 } 232 233 int 234 simplefb_wsioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 235 { 236 struct rasops_info *ri = v; 237 struct simplefb_softc *sc = ri->ri_hw; 238 struct wsdisplay_param *dp = (struct wsdisplay_param *)data; 239 struct wsdisplay_fbinfo *wdf; 240 241 switch (cmd) { 242 case WSDISPLAYIO_GETPARAM: 243 if (ws_get_param) 244 return ws_get_param(dp); 245 return -1; 246 case WSDISPLAYIO_SETPARAM: 247 if (ws_set_param) 248 return ws_set_param(dp); 249 return -1; 250 case WSDISPLAYIO_GTYPE: 251 *(u_int *)data = WSDISPLAY_TYPE_EFIFB; 252 return 0; 253 case WSDISPLAYIO_GINFO: 254 wdf = (struct wsdisplay_fbinfo *)data; 255 wdf->width = ri->ri_width; 256 wdf->height = ri->ri_height; 257 wdf->depth = ri->ri_depth; 258 wdf->stride = ri->ri_stride; 259 wdf->offset = sc->sc_paddr & PAGE_MASK; 260 wdf->cmsize = 0; /* color map is unavailable */ 261 break; 262 case WSDISPLAYIO_LINEBYTES: 263 *(u_int *)data = ri->ri_stride; 264 break; 265 case WSDISPLAYIO_SMODE: 266 break; 267 case WSDISPLAYIO_GETSUPPORTEDDEPTH: 268 switch (ri->ri_depth) { 269 case 32: 270 if (ri->ri_rnum == 10) 271 *(u_int *)data = WSDISPLAYIO_DEPTH_30; 272 else 273 *(u_int *)data = WSDISPLAYIO_DEPTH_24_32; 274 break; 275 case 24: 276 *(u_int *)data = WSDISPLAYIO_DEPTH_24_24; 277 break; 278 case 16: 279 *(u_int *)data = WSDISPLAYIO_DEPTH_16; 280 break; 281 case 15: 282 *(u_int *)data = WSDISPLAYIO_DEPTH_15; 283 break; 284 default: 285 return -1; 286 } 287 break; 288 default: 289 return -1; 290 } 291 292 return 0; 293 } 294 295 paddr_t 296 simplefb_wsmmap(void *v, off_t off, int prot) 297 { 298 struct rasops_info *ri = v; 299 struct simplefb_softc *sc = ri->ri_hw; 300 301 if (off < 0 || off >= sc->sc_psize) 302 return -1; 303 304 return ((sc->sc_paddr + off) | PMAP_NOCACHE); 305 } 306 307 int 308 simplefb_alloc_screen(void *v, const struct wsscreen_descr *type, 309 void **cookiep, int *curxp, int *curyp, uint32_t *attrp) 310 { 311 return rasops_alloc_screen(v, cookiep, curxp, curyp, attrp); 312 } 313 314 #include "ukbd.h" 315 316 #if NUKBD > 0 317 #include <dev/usb/ukbdvar.h> 318 #endif 319 320 void 321 simplefb_init_cons(bus_space_tag_t iot) 322 { 323 struct rasops_info *ri = &simplefb_ri; 324 bus_space_handle_t ioh; 325 struct fdt_reg reg; 326 void *node; 327 uint32_t defattr = 0; 328 329 node = fdt_find_cons("simple-framebuffer"); 330 if (node == NULL) 331 return; 332 333 if (fdt_get_reg(node, 0, ®)) 334 return; 335 336 if (bus_space_map(iot, reg.addr, reg.size, 337 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, &ioh)) 338 return; 339 340 ri->ri_bits = bus_space_vaddr(iot, ioh); 341 342 if (simplefb_init(stdout_node, ri)) 343 return; 344 345 ri->ri_bs = simplefb_bs; 346 rasops_init(ri, SIMPLEFB_HEIGHT, SIMPLEFB_WIDTH); 347 348 simplefb_wsd.capabilities = ri->ri_caps; 349 simplefb_wsd.ncols = ri->ri_cols; 350 simplefb_wsd.nrows = ri->ri_rows; 351 simplefb_wsd.textops = &ri->ri_ops; 352 simplefb_wsd.fontwidth = ri->ri_font->fontwidth; 353 simplefb_wsd.fontheight = ri->ri_font->fontheight; 354 355 ri->ri_ops.pack_attr(ri, 0, 0, 0, &defattr); 356 wsdisplay_cnattach(&simplefb_wsd, ri, 0, 0, defattr); 357 358 #if NUKBD > 0 359 /* Allow USB keyboards to become the console input device. */ 360 ukbd_cnattach(); 361 #endif 362 } 363