1 /* $NetBSD: genfb.c,v 1.12 2007/11/19 04:00:39 macallan Exp $ */ 2 3 /*- 4 * Copyright (c) 2007 Michael Lorenz 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of The NetBSD Foundation nor the names of its 16 * contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: genfb.c,v 1.12 2007/11/19 04:00:39 macallan Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/kernel.h> 38 #include <sys/device.h> 39 #include <sys/proc.h> 40 #include <sys/mutex.h> 41 #include <sys/ioctl.h> 42 #include <sys/kernel.h> 43 #include <sys/systm.h> 44 #include <sys/malloc.h> 45 46 #include <dev/wscons/wsconsio.h> 47 #include <dev/wscons/wsdisplayvar.h> 48 #include <dev/rasops/rasops.h> 49 #include <dev/wsfont/wsfont.h> 50 51 #include <dev/wscons/wsdisplay_vconsvar.h> 52 53 #include <dev/wsfb/genfbvar.h> 54 55 #include "opt_genfb.h" 56 #include "opt_wsfb.h" 57 58 #ifdef GENFB_DEBUG 59 #define GPRINTF panic 60 #else 61 #define GPRINTF aprint_verbose 62 #endif 63 64 static int genfb_ioctl(void *, void *, u_long, void *, int, struct lwp *); 65 static paddr_t genfb_mmap(void *, void *, off_t, int); 66 static void genfb_init_screen(void *, struct vcons_screen *, int, long *); 67 68 static int genfb_putcmap(struct genfb_softc *, struct wsdisplay_cmap *); 69 static int genfb_getcmap(struct genfb_softc *, struct wsdisplay_cmap *); 70 static void genfb_restore_palette(struct genfb_softc *); 71 static int genfb_putpalreg(struct genfb_softc *, uint8_t, uint8_t, 72 uint8_t, uint8_t); 73 74 extern const u_char rasops_cmap[768]; 75 76 struct wsdisplay_accessops genfb_accessops = { 77 genfb_ioctl, 78 genfb_mmap, 79 NULL, /* alloc_screen */ 80 NULL, /* free_screen */ 81 NULL, /* show_screen */ 82 NULL, /* load_font */ 83 NULL, /* pollc */ 84 NULL /* scroll */ 85 }; 86 87 void 88 genfb_init(struct genfb_softc *sc) 89 { 90 prop_dictionary_t dict; 91 uint64_t cmap_cb; 92 uint32_t fboffset; 93 94 dict = device_properties(&sc->sc_dev); 95 #ifdef GENFB_DEBUG 96 printf(prop_dictionary_externalize(dict)); 97 #endif 98 if (!prop_dictionary_get_uint32(dict, "width", &sc->sc_width)) { 99 GPRINTF("no width property\n"); 100 return; 101 } 102 if (!prop_dictionary_get_uint32(dict, "height", &sc->sc_height)) { 103 GPRINTF("no height property\n"); 104 return; 105 } 106 if (!prop_dictionary_get_uint32(dict, "depth", &sc->sc_depth)) { 107 GPRINTF("no depth property\n"); 108 return; 109 } 110 111 /* XXX should be a 64bit value */ 112 if (!prop_dictionary_get_uint32(dict, "address", &fboffset)) { 113 GPRINTF("no address property\n"); 114 return; 115 } 116 117 sc->sc_fboffset = fboffset; 118 119 if (!prop_dictionary_get_uint32(dict, "linebytes", &sc->sc_stride)) 120 sc->sc_stride = (sc->sc_width * sc->sc_depth) >> 3; 121 sc->sc_fbsize = sc->sc_height * sc->sc_stride; 122 123 /* optional colour map callback */ 124 sc->sc_cmcb = NULL; 125 if (prop_dictionary_get_uint64(dict, "cmap_callback", &cmap_cb)) { 126 if (cmap_cb != 0) 127 sc->sc_cmcb = (void *)(vaddr_t)cmap_cb; 128 } 129 } 130 131 int 132 genfb_attach(struct genfb_softc *sc, struct genfb_ops *ops) 133 { 134 struct wsemuldisplaydev_attach_args aa; 135 prop_dictionary_t dict; 136 struct rasops_info *ri; 137 long defattr; 138 int i, j; 139 bool console; 140 141 aprint_verbose("%s: framebuffer at %p, size %dx%d, depth %d, " 142 "stride %d\n", sc->sc_dev.dv_xname, sc->sc_fbaddr, 143 sc->sc_width, sc->sc_height, sc->sc_depth, sc->sc_stride); 144 145 sc->sc_defaultscreen_descr = (struct wsscreen_descr){ 146 "default", 147 0, 0, 148 NULL, 149 8, 16, 150 WSSCREEN_WSCOLORS | WSSCREEN_HILIT, 151 NULL 152 }; 153 sc->sc_screens[0] = &sc->sc_defaultscreen_descr; 154 sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens}; 155 memcpy(&sc->sc_ops, ops, sizeof(struct genfb_ops)); 156 sc->sc_mode = WSDISPLAYIO_MODE_EMUL; 157 158 sc->sc_shadowfb = malloc(sc->sc_fbsize, M_DEVBUF, M_WAITOK); 159 160 dict = device_properties(&sc->sc_dev); 161 162 prop_dictionary_get_bool(dict, "is_console", &console); 163 164 vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr, 165 &genfb_accessops); 166 sc->vd.init_screen = genfb_init_screen; 167 168 /* Do not print anything between this point and the screen 169 * clear operation below. Otherwise it will be lost. */ 170 171 ri = &sc->sc_console_screen.scr_ri; 172 173 if (console) { 174 vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1, 175 &defattr); 176 sc->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC; 177 178 sc->sc_defaultscreen_descr.textops = &ri->ri_ops; 179 sc->sc_defaultscreen_descr.capabilities = ri->ri_caps; 180 sc->sc_defaultscreen_descr.nrows = ri->ri_rows; 181 sc->sc_defaultscreen_descr.ncols = ri->ri_cols; 182 wsdisplay_cnattach(&sc->sc_defaultscreen_descr, ri, 0, 0, 183 defattr); 184 } else { 185 /* 186 * since we're not the console we can postpone the rest 187 * until someone actually allocates a screen for us 188 */ 189 (*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr); 190 } 191 192 /* Clear the whole screen to bring it to a known state. */ 193 (*ri->ri_ops.eraserows)(ri, 0, ri->ri_rows, defattr); 194 195 j = 0; 196 for (i = 0; i < (1 << sc->sc_depth); i++) { 197 198 sc->sc_cmap_red[i] = rasops_cmap[j]; 199 sc->sc_cmap_green[i] = rasops_cmap[j + 1]; 200 sc->sc_cmap_blue[i] = rasops_cmap[j + 2]; 201 genfb_putpalreg(sc, i, rasops_cmap[j], rasops_cmap[j + 1], 202 rasops_cmap[j + 2]); 203 j += 3; 204 } 205 206 aa.console = console; 207 aa.scrdata = &sc->sc_screenlist; 208 aa.accessops = &genfb_accessops; 209 aa.accesscookie = &sc->vd; 210 211 config_found(&sc->sc_dev, &aa, wsemuldisplaydevprint); 212 213 return 0; 214 } 215 216 static int 217 genfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 218 struct lwp *l) 219 { 220 struct vcons_data *vd = v; 221 struct genfb_softc *sc = vd->cookie; 222 struct wsdisplay_fbinfo *wdf; 223 struct vcons_screen *ms = vd->active; 224 225 switch (cmd) { 226 227 case WSDISPLAYIO_GINFO: 228 if (ms == NULL) 229 return ENODEV; 230 wdf = (void *)data; 231 wdf->height = ms->scr_ri.ri_height; 232 wdf->width = ms->scr_ri.ri_width; 233 wdf->depth = ms->scr_ri.ri_depth; 234 wdf->cmsize = 256; 235 return 0; 236 237 case WSDISPLAYIO_GETCMAP: 238 return genfb_getcmap(sc, 239 (struct wsdisplay_cmap *)data); 240 241 case WSDISPLAYIO_PUTCMAP: 242 return genfb_putcmap(sc, 243 (struct wsdisplay_cmap *)data); 244 245 case WSDISPLAYIO_LINEBYTES: 246 *(u_int *)data = sc->sc_stride; 247 return 0; 248 249 case WSDISPLAYIO_SMODE: 250 { 251 int new_mode = *(int*)data; 252 253 /* notify the bus backend */ 254 if (sc->sc_ops.genfb_ioctl) 255 return sc->sc_ops.genfb_ioctl(sc, vs, 256 cmd, data, flag, l); 257 258 if (new_mode != sc->sc_mode) { 259 sc->sc_mode = new_mode; 260 if(new_mode == WSDISPLAYIO_MODE_EMUL) { 261 genfb_restore_palette(sc); 262 vcons_redraw_screen(ms); 263 } 264 } 265 } 266 return 0; 267 default: 268 if (sc->sc_ops.genfb_ioctl) 269 return sc->sc_ops.genfb_ioctl(sc, vs, cmd, 270 data, flag, l); 271 } 272 return EPASSTHROUGH; 273 } 274 275 static paddr_t 276 genfb_mmap(void *v, void *vs, off_t offset, int prot) 277 { 278 struct vcons_data *vd = v; 279 struct genfb_softc *sc = vd->cookie; 280 281 if (sc->sc_ops.genfb_mmap) 282 return sc->sc_ops.genfb_mmap(sc, vs, offset, prot); 283 284 return -1; 285 } 286 287 static void 288 genfb_init_screen(void *cookie, struct vcons_screen *scr, 289 int existing, long *defattr) 290 { 291 struct genfb_softc *sc = cookie; 292 struct rasops_info *ri = &scr->scr_ri; 293 294 ri->ri_depth = sc->sc_depth; 295 ri->ri_width = sc->sc_width; 296 ri->ri_height = sc->sc_height; 297 ri->ri_stride = sc->sc_stride; 298 ri->ri_flg = RI_CENTER | RI_FULLCLEAR; 299 300 if (sc->sc_shadowfb != NULL) { 301 302 ri->ri_hwbits = (char *)sc->sc_fbaddr; 303 ri->ri_bits = (char *)sc->sc_shadowfb; 304 } else 305 ri->ri_bits = (char *)sc->sc_fbaddr; 306 307 if (existing) { 308 ri->ri_flg |= RI_CLEAR; 309 } 310 311 rasops_init(ri, sc->sc_height / 8, sc->sc_width / 8); 312 ri->ri_caps = WSSCREEN_WSCOLORS; 313 314 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight, 315 sc->sc_width / ri->ri_font->fontwidth); 316 317 ri->ri_hw = scr; 318 } 319 320 static int 321 genfb_putcmap(struct genfb_softc *sc, struct wsdisplay_cmap *cm) 322 { 323 u_char *r, *g, *b; 324 u_int index = cm->index; 325 u_int count = cm->count; 326 int i, error; 327 u_char rbuf[256], gbuf[256], bbuf[256]; 328 329 #ifdef GENFB_DEBUG 330 aprint_debug("putcmap: %d %d\n",index, count); 331 #endif 332 if (cm->index >= 256 || cm->count > 256 || 333 (cm->index + cm->count) > 256) 334 return EINVAL; 335 error = copyin(cm->red, &rbuf[index], count); 336 if (error) 337 return error; 338 error = copyin(cm->green, &gbuf[index], count); 339 if (error) 340 return error; 341 error = copyin(cm->blue, &bbuf[index], count); 342 if (error) 343 return error; 344 345 memcpy(&sc->sc_cmap_red[index], &rbuf[index], count); 346 memcpy(&sc->sc_cmap_green[index], &gbuf[index], count); 347 memcpy(&sc->sc_cmap_blue[index], &bbuf[index], count); 348 349 r = &sc->sc_cmap_red[index]; 350 g = &sc->sc_cmap_green[index]; 351 b = &sc->sc_cmap_blue[index]; 352 353 for (i = 0; i < count; i++) { 354 genfb_putpalreg(sc, index, *r, *g, *b); 355 index++; 356 r++, g++, b++; 357 } 358 return 0; 359 } 360 361 static int 362 genfb_getcmap(struct genfb_softc *sc, struct wsdisplay_cmap *cm) 363 { 364 u_int index = cm->index; 365 u_int count = cm->count; 366 int error; 367 368 if (index >= 255 || count > 256 || index + count > 256) 369 return EINVAL; 370 371 error = copyout(&sc->sc_cmap_red[index], cm->red, count); 372 if (error) 373 return error; 374 error = copyout(&sc->sc_cmap_green[index], cm->green, count); 375 if (error) 376 return error; 377 error = copyout(&sc->sc_cmap_blue[index], cm->blue, count); 378 if (error) 379 return error; 380 381 return 0; 382 } 383 384 static void 385 genfb_restore_palette(struct genfb_softc *sc) 386 { 387 int i; 388 389 for (i = 0; i < (1 << sc->sc_depth); i++) { 390 genfb_putpalreg(sc, i, sc->sc_cmap_red[i], 391 sc->sc_cmap_green[i], sc->sc_cmap_blue[i]); 392 } 393 } 394 395 static int 396 genfb_putpalreg(struct genfb_softc *sc, uint8_t idx, uint8_t r, uint8_t g, 397 uint8_t b) 398 { 399 400 if (sc->sc_cmcb) { 401 402 sc->sc_cmcb->gcc_set_mapreg(sc->sc_cmcb->gcc_cookie, 403 idx, r, g, b); 404 } 405 return 0; 406 } 407 408