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