1 /* $NetBSD: fb.c,v 1.32 2009/03/18 16:00:20 cegger Exp $ */ 2 3 /* 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This software was developed by the Computer Systems Engineering group 8 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 9 * contributed to Berkeley. 10 * 11 * All advertising materials mentioning features or use of this software 12 * must display the following acknowledgement: 13 * This product includes software developed by the University of 14 * California, Lawrence Berkeley Laboratory. 15 * 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions 18 * are met: 19 * 1. Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution. 24 * 3. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 * 40 * @(#)fb.c 8.1 (Berkeley) 6/11/93 41 */ 42 43 /* 44 * /dev/fb (indirect frame buffer driver). This is gross; we should 45 * just build cdevsw[] dynamically. 46 */ 47 48 #include <sys/cdefs.h> 49 __KERNEL_RCSID(0, "$NetBSD: fb.c,v 1.32 2009/03/18 16:00:20 cegger Exp $"); 50 51 #include <sys/param.h> 52 #include <sys/systm.h> 53 #include <sys/device.h> 54 #include <sys/proc.h> 55 #include <sys/conf.h> 56 #include <sys/malloc.h> 57 #include <sys/types.h> 58 59 #include <machine/promlib.h> 60 #include <machine/autoconf.h> 61 #include <machine/kbd.h> 62 #include <machine/eeprom.h> 63 #include <sparc/dev/cons.h> 64 65 #include <dev/sun/fbio.h> 66 #include <dev/sun/fbvar.h> 67 68 #include "kbd.h" 69 #include "pfour.h" 70 71 72 struct fbdevlist { 73 struct fbdevice *fb_dev; 74 struct fbdevlist *fb_next; 75 }; 76 77 static struct fbdevlist fblist = { 78 NULL, 79 NULL, 80 }; 81 82 dev_type_open(fbopen); 83 dev_type_close(fbclose); 84 dev_type_ioctl(fbioctl); 85 dev_type_poll(fbpoll); 86 dev_type_mmap(fbmmap); 87 dev_type_kqfilter(fbkqfilter); 88 89 const struct cdevsw fb_cdevsw = { 90 fbopen, fbclose, noread, nowrite, fbioctl, 91 nostop, notty, fbpoll, fbmmap, fbkqfilter, D_OTHER 92 }; 93 94 void 95 fb_unblank(void) 96 { 97 98 struct fbdevlist *fbl = &fblist; 99 100 while (fbl != NULL && fbl->fb_dev != NULL) { 101 (*fbl->fb_dev->fb_driver->fbd_unblank)(fbl->fb_dev->fb_device); 102 fbl = fbl->fb_next; 103 } 104 } 105 106 /* 107 * Helper function for frame buffer devices. Decides whether 108 * the device can be the console output device according to 109 * PROM info. The result from this function may not be conclusive 110 * on machines with old PROMs; in that case, drivers should consult 111 * other sources of configuration information (e.g. EEPROM entries). 112 */ 113 int 114 fb_is_console(int node) 115 { 116 #if !defined(SUN4U) 117 int fbnode; 118 119 switch (prom_version()) { 120 case PROM_OLDMON: 121 /* `node' is not valid; just check for any fb device */ 122 return (prom_stdout() == PROMDEV_SCREEN); 123 124 case PROM_OBP_V0: 125 /* 126 * First, check if prom_stdout() represents a frame buffer, 127 * then match on the `fb' property on the root node, if any. 128 */ 129 if (prom_stdout() != PROMDEV_SCREEN) 130 return (0); 131 132 fbnode = prom_getpropint(findroot(), "fb", 0); 133 return (fbnode == 0 || node == fbnode); 134 135 case PROM_OBP_V2: 136 case PROM_OBP_V3: 137 case PROM_OPENFIRM: 138 /* Just match the nodes */ 139 return (node == prom_stdout_node); 140 } 141 142 return (0); 143 #else 144 return (node == prom_stdout_node); 145 #endif 146 } 147 148 void 149 fb_attach(struct fbdevice *fb, int isconsole) 150 { 151 static int seen_force = 0; 152 int nfb = 0; 153 struct fbdevlist *fbl = &fblist; 154 155 /* 156 * Check to see if we're being forced into /dev/fb0, or if we're 157 * the console. If we are, then move/replace the current fb0. 158 */ 159 if ((fb->fb_flags & FB_FORCE || (isconsole && !seen_force)) && 160 fblist.fb_dev != NULL) { 161 while (fbl->fb_next != NULL) { 162 fbl = fbl->fb_next; 163 nfb++; 164 } 165 if ((fbl->fb_next = malloc(sizeof (struct fbdevlist), 166 M_DEVBUF, M_NOWAIT)) == NULL) 167 printf("%s: replacing %s at /dev/fb0\n", 168 device_xname(fb->fb_device), 169 device_xname(fblist.fb_dev->fb_device)); 170 else { 171 fbl = fbl->fb_next; 172 nfb++; 173 fbl->fb_dev = fblist.fb_dev; 174 fbl->fb_next = NULL; 175 printf("%s: moved to /dev/fb%d\n", 176 device_xname(fbl->fb_dev->fb_device), nfb); 177 printf("%s: attached to /dev/fb0\n", 178 device_xname(fb->fb_device)); 179 } 180 fblist.fb_dev = fb; 181 if (fb->fb_flags & FB_FORCE) 182 seen_force = 1; 183 /* Add to end of fb list. */ 184 } else { 185 if (fblist.fb_dev != NULL) { 186 while (fbl->fb_next != NULL) { 187 fbl = fbl->fb_next; 188 nfb++; 189 } 190 if ((fbl->fb_next = malloc(sizeof (struct fbdevlist), 191 M_DEVBUF, M_NOWAIT)) == NULL) { 192 aprint_error_dev(fb->fb_device, "no space to attach after /dev/fb%d\n", 193 nfb); 194 return; 195 } 196 fbl = fbl->fb_next; 197 nfb++; 198 } 199 fbl->fb_dev = fb; 200 fbl->fb_next = NULL; 201 printf("%s: attached to /dev/fb%d\n", 202 device_xname(fbl->fb_dev->fb_device), nfb); 203 } 204 } 205 206 int 207 fbopen(dev_t dev, int flags, int mode, struct lwp *l) 208 { 209 int unit, nunit; 210 struct fbdevlist *fbl = &fblist; 211 212 unit = minor(dev); 213 while (unit-- && fbl != NULL) 214 fbl = fbl->fb_next; 215 if (fbl == NULL || fbl->fb_dev == NULL) 216 return (ENXIO); 217 218 nunit = device_unit(fbl->fb_dev->fb_device); 219 return (fbl->fb_dev->fb_driver->fbd_open)(makedev(0, nunit), flags, 220 mode, l); 221 } 222 223 int 224 fbclose(dev_t dev, int flags, int mode, struct lwp *l) 225 { 226 int unit, nunit; 227 struct fbdevlist *fbl = &fblist; 228 229 unit = minor(dev); 230 while (unit-- && fbl != NULL) 231 fbl = fbl->fb_next; 232 if (fbl == NULL || fbl->fb_dev == NULL) 233 return (ENXIO); 234 235 nunit = device_unit(fbl->fb_dev->fb_device); 236 return (fbl->fb_dev->fb_driver->fbd_close)(makedev(0, nunit), flags, 237 mode, l); 238 } 239 240 int 241 fbioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l) 242 { 243 int unit, nunit; 244 struct fbdevlist *fbl = &fblist; 245 246 unit = minor(dev); 247 while (unit-- && fbl != NULL) 248 fbl = fbl->fb_next; 249 if (fbl == NULL || fbl->fb_dev == NULL) 250 return (ENXIO); 251 252 nunit = device_unit(fbl->fb_dev->fb_device); 253 return (fbl->fb_dev->fb_driver->fbd_ioctl)(makedev(0, nunit), cmd, 254 data, flags, l); 255 } 256 257 int 258 fbpoll(dev_t dev, int events, struct lwp *l) 259 { 260 int unit, nunit; 261 struct fbdevlist *fbl = &fblist; 262 263 unit = minor(dev); 264 while (unit-- && fbl != NULL) 265 fbl = fbl->fb_next; 266 if (fbl == NULL || fbl->fb_dev == NULL) 267 return (ENXIO); 268 269 nunit = device_unit(fbl->fb_dev->fb_device); 270 return (fbl->fb_dev->fb_driver->fbd_poll)(makedev(0, nunit), events, 271 l); 272 } 273 274 int 275 fbkqfilter(dev_t dev, struct knote *kn) 276 { 277 int unit, nunit; 278 struct fbdevlist *fbl = &fblist; 279 280 unit = minor(dev); 281 while (unit-- && fbl != NULL) 282 fbl = fbl->fb_next; 283 if (fbl == NULL || fbl->fb_dev == NULL) 284 return (ENXIO); 285 286 nunit = device_unit(fbl->fb_dev->fb_device); 287 return (fbl->fb_dev->fb_driver->fbd_kqfilter)(makedev(0, nunit), kn); 288 } 289 290 paddr_t 291 fbmmap(dev_t dev, off_t off, int prot) 292 { 293 int unit, nunit; 294 struct fbdevlist *fbl = &fblist; 295 296 unit = minor(dev); 297 while (unit-- && fbl != NULL) 298 fbl = fbl->fb_next; 299 if (fbl == NULL || fbl->fb_dev == NULL) 300 return (ENXIO); 301 302 nunit = device_unit(fbl->fb_dev->fb_device); 303 paddr_t (*map)(dev_t, off_t, int) = fbl->fb_dev->fb_driver->fbd_mmap; 304 305 if (map == NULL) 306 return (-1); 307 return (map(makedev(0, nunit), off, prot)); 308 } 309 310 void 311 fb_setsize_obp(struct fbdevice *fb, int depth, int def_width, int def_height, int node) 312 { 313 fb->fb_type.fb_width = prom_getpropint(node, "width", def_width); 314 fb->fb_type.fb_height = prom_getpropint(node, "height", def_height); 315 fb->fb_linebytes = prom_getpropint(node, "linebytes", 316 (fb->fb_type.fb_width * depth) / 8); 317 } 318 319 void 320 fb_setsize_eeprom(struct fbdevice *fb, int depth, int def_width, int def_height) 321 { 322 #if !defined(SUN4U) 323 struct eeprom *eep = (struct eeprom *)eeprom_va; 324 325 if (!CPU_ISSUN4) { 326 printf("fb_setsize_eeprom: not sun4\n"); 327 return; 328 } 329 330 /* Set up some defaults. */ 331 fb->fb_type.fb_width = def_width; 332 fb->fb_type.fb_height = def_height; 333 334 if (fb->fb_flags & FB_PFOUR) { 335 #if NPFOUR > 0 336 fb_setsize_pfour(fb); 337 #endif 338 } else if (eep != NULL) { 339 switch (eep->eeScreenSize) { 340 case EE_SCR_1152X900: 341 fb->fb_type.fb_width = 1152; 342 fb->fb_type.fb_height = 900; 343 break; 344 345 case EE_SCR_1024X1024: 346 fb->fb_type.fb_width = 1024; 347 fb->fb_type.fb_height = 1024; 348 break; 349 350 case EE_SCR_1600X1280: 351 fb->fb_type.fb_width = 1600; 352 fb->fb_type.fb_height = 1280; 353 break; 354 355 case EE_SCR_1440X1440: 356 fb->fb_type.fb_width = 1440; 357 fb->fb_type.fb_height = 1440; 358 break; 359 360 default: 361 /* 362 * XXX: Do nothing, I guess. 363 * Should we print a warning about 364 * an unknown value? --thorpej 365 */ 366 break; 367 } 368 } 369 370 fb->fb_linebytes = (fb->fb_type.fb_width * depth) / 8; 371 #endif /* !SUN4U */ 372 } 373 374 375 376 #ifdef RASTERCONSOLE 377 static void fb_bell(int); 378 379 static void 380 fb_bell(int on) 381 { 382 #if NKBD > 0 383 kbd_bell(on); 384 #endif 385 } 386 387 void 388 fbrcons_init(struct fbdevice *fb) 389 { 390 struct rconsole *rc = &fb->fb_rcons; 391 struct rasops_info *ri = &fb->fb_rinfo; 392 int maxrow, maxcol; 393 #if !defined(RASTERCONS_FULLSCREEN) 394 int *row, *col; 395 #endif 396 397 /* Set up what rasops needs to know about */ 398 memset(ri, 0, sizeof *ri); 399 ri->ri_stride = fb->fb_linebytes; 400 ri->ri_bits = (void *)fb->fb_pixels; 401 ri->ri_depth = fb->fb_type.fb_depth; 402 ri->ri_width = fb->fb_type.fb_width; 403 ri->ri_height = fb->fb_type.fb_height; 404 maxrow = 5000; 405 maxcol = 5000; 406 407 #if !defined(RASTERCONS_FULLSCREEN) 408 #if !defined(SUN4U) 409 if (CPU_ISSUN4) { 410 struct eeprom *eep = (struct eeprom *)eeprom_va; 411 412 if (eep == NULL) { 413 maxcol = 80; 414 maxrow = 34; 415 } else { 416 maxcol = eep->eeTtyCols; 417 maxrow = eep->eeTtyRows; 418 } 419 } 420 #endif /* !SUN4U */ 421 if (!CPU_ISSUN4) { 422 char buf[6+1]; /* Enough for six digits */ 423 maxcol = (prom_getoption("screen-#columns", buf, sizeof buf) == 0) 424 ? strtoul(buf, NULL, 10) 425 : 80; 426 427 maxrow = (prom_getoption("screen-#rows", buf, sizeof buf) != 0) 428 ? strtoul(buf, NULL, 10) 429 : 34; 430 431 } 432 #endif /* !RASTERCONS_FULLSCREEN */ 433 /* 434 * - force monochrome output 435 * - eraserows() hack to clear the *entire* display 436 * - cursor is currently enabled 437 * - center output 438 */ 439 ri->ri_flg = RI_FULLCLEAR | RI_CURSOR | RI_CENTER; 440 441 /* Get operations set and connect to rcons */ 442 if (rasops_init(ri, maxrow, maxcol)) 443 panic("fbrcons_init: rasops_init failed!"); 444 445 if (ri->ri_depth == 8) { 446 int i; 447 for (i = 0; i < 16; i++) { 448 449 /* 450 * Cmap entries are repeated four times in the 451 * 32 bit wide `devcmap' entries for optimization 452 * purposes; see rasops(9) 453 */ 454 #define I_TO_DEVCMAP(i) ((i) | ((i)<<8) | ((i)<<16) | ((i)<<24)) 455 456 /* 457 * Use existing colormap entries for black and white 458 */ 459 if ((i & 7) == WSCOL_BLACK) { 460 ri->ri_devcmap[i] = I_TO_DEVCMAP(255); 461 continue; 462 } 463 464 if ((i & 7) == WSCOL_WHITE) { 465 ri->ri_devcmap[i] = I_TO_DEVCMAP(0); 466 continue; 467 } 468 /* 469 * Other entries refer to ANSI map, which for now 470 * is setup in bt_subr.c 471 */ 472 ri->ri_devcmap[i] = I_TO_DEVCMAP(i + 1); 473 #undef I_TO_DEVCMAP 474 } 475 } 476 477 rc->rc_row = rc->rc_col = 0; 478 #if !defined(RASTERCONS_FULLSCREEN) 479 /* Determine addresses of prom emulator row and column */ 480 if (!CPU_ISSUN4 && !romgetcursoraddr(&row, &col)) { 481 rc->rc_row = *row; 482 rc->rc_col = *col; 483 } 484 #endif 485 ri->ri_crow = rc->rc_row; 486 ri->ri_ccol = rc->rc_col; 487 488 rc->rc_ops = &ri->ri_ops; 489 rc->rc_cookie = ri; 490 rc->rc_bell = fb_bell; 491 rc->rc_maxcol = ri->ri_cols; 492 rc->rc_maxrow = ri->ri_rows; 493 rc->rc_width = ri->ri_emuwidth; 494 rc->rc_height = ri->ri_emuheight; 495 rc->rc_deffgcolor = WSCOL_BLACK; 496 rc->rc_defbgcolor = WSCOL_WHITE; 497 rcons_init(rc, 0); 498 499 /* Hook up virtual console */ 500 v_putc = rcons_cnputc; 501 } 502 503 int 504 fbrcons_rows(void) 505 { 506 return ((fblist.fb_dev != NULL) ? 507 fblist.fb_dev->fb_rcons.rc_maxrow : 0); 508 } 509 510 int 511 fbrcons_cols(void) 512 { 513 return ((fblist.fb_dev != NULL) ? 514 fblist.fb_dev->fb_rcons.rc_maxcol : 0); 515 } 516 #endif /* RASTERCONSOLE */ 517