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 72struct fbdevlist { 73 struct fbdevice *fb_dev; 74 struct fbdevlist *fb_next; 75}; 76 77static struct fbdevlist fblist = { 78 NULL, 79 NULL, 80}; 81 82dev_type_open(fbopen); 83dev_type_close(fbclose); 84dev_type_ioctl(fbioctl); 85dev_type_poll(fbpoll); 86dev_type_mmap(fbmmap); 87dev_type_kqfilter(fbkqfilter); 88 89const struct cdevsw fb_cdevsw = { 90 fbopen, fbclose, noread, nowrite, fbioctl, 91 nostop, notty, fbpoll, fbmmap, fbkqfilter, D_OTHER 92}; 93 94void 95fb_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 */ 113int 114fb_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 148void 149fb_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 aprint_normal_dev(fbl->fb_dev->fb_device, 176 "moved to /dev/fb%d\n", nfb); 177 aprint_normal_dev(fbl->fb_dev->fb_device, 178 "attached to /dev/fb0\n"); 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, 193 "no space to attach after /dev/fb%d\n", 194 nfb); 195 return; 196 } 197 fbl = fbl->fb_next; 198 nfb++; 199 } 200 fbl->fb_dev = fb; 201 fbl->fb_next = NULL; 202 aprint_normal_dev(fbl->fb_dev->fb_device, 203 "attached to /dev/fb%d\n", nfb); 204 } 205} 206 207int 208fbopen(dev_t dev, int flags, int mode, struct lwp *l) 209{ 210 int unit, nunit; 211 struct fbdevlist *fbl = &fblist; 212 213 unit = minor(dev); 214 while (unit-- && fbl != NULL) 215 fbl = fbl->fb_next; 216 if (fbl == NULL || fbl->fb_dev == NULL) 217 return (ENXIO); 218 219 nunit = device_unit(fbl->fb_dev->fb_device); 220 return (fbl->fb_dev->fb_driver->fbd_open)(makedev(0, nunit), flags, 221 mode, l); 222} 223 224int 225fbclose(dev_t dev, int flags, int mode, struct lwp *l) 226{ 227 int unit, nunit; 228 struct fbdevlist *fbl = &fblist; 229 230 unit = minor(dev); 231 while (unit-- && fbl != NULL) 232 fbl = fbl->fb_next; 233 if (fbl == NULL || fbl->fb_dev == NULL) 234 return (ENXIO); 235 236 nunit = device_unit(fbl->fb_dev->fb_device); 237 return (fbl->fb_dev->fb_driver->fbd_close)(makedev(0, nunit), flags, 238 mode, l); 239} 240 241int 242fbioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l) 243{ 244 int unit, nunit; 245 struct fbdevlist *fbl = &fblist; 246 247 unit = minor(dev); 248 while (unit-- && fbl != NULL) 249 fbl = fbl->fb_next; 250 if (fbl == NULL || fbl->fb_dev == NULL) 251 return (ENXIO); 252 253 nunit = device_unit(fbl->fb_dev->fb_device); 254 return (fbl->fb_dev->fb_driver->fbd_ioctl)(makedev(0, nunit), cmd, 255 data, flags, l); 256} 257 258int 259fbpoll(dev_t dev, int events, struct lwp *l) 260{ 261 int unit, nunit; 262 struct fbdevlist *fbl = &fblist; 263 264 unit = minor(dev); 265 while (unit-- && fbl != NULL) 266 fbl = fbl->fb_next; 267 if (fbl == NULL || fbl->fb_dev == NULL) 268 return (ENXIO); 269 270 nunit = device_unit(fbl->fb_dev->fb_device); 271 return (fbl->fb_dev->fb_driver->fbd_poll)(makedev(0, nunit), events, 272 l); 273} 274 275int 276fbkqfilter(dev_t dev, struct knote *kn) 277{ 278 int unit, nunit; 279 struct fbdevlist *fbl = &fblist; 280 281 unit = minor(dev); 282 while (unit-- && fbl != NULL) 283 fbl = fbl->fb_next; 284 if (fbl == NULL || fbl->fb_dev == NULL) 285 return (ENXIO); 286 287 nunit = device_unit(fbl->fb_dev->fb_device); 288 return (fbl->fb_dev->fb_driver->fbd_kqfilter)(makedev(0, nunit), kn); 289} 290 291paddr_t 292fbmmap(dev_t dev, off_t off, int prot) 293{ 294 int unit, nunit; 295 struct fbdevlist *fbl = &fblist; 296 297 unit = minor(dev); 298 while (unit-- && fbl != NULL) 299 fbl = fbl->fb_next; 300 if (fbl == NULL || fbl->fb_dev == NULL) 301 return (ENXIO); 302 303 nunit = device_unit(fbl->fb_dev->fb_device); 304 paddr_t (*map)(dev_t, off_t, int) = fbl->fb_dev->fb_driver->fbd_mmap; 305 306 if (map == NULL) 307 return (-1); 308 return (map(makedev(0, nunit), off, prot)); 309} 310 311void 312fb_setsize_obp(struct fbdevice *fb, int depth, int def_width, int def_height, int node) 313{ 314 fb->fb_type.fb_width = prom_getpropint(node, "width", def_width); 315 fb->fb_type.fb_height = prom_getpropint(node, "height", def_height); 316 fb->fb_linebytes = prom_getpropint(node, "linebytes", 317 (fb->fb_type.fb_width * depth) / 8); 318} 319 320void 321fb_setsize_eeprom(struct fbdevice *fb, int depth, int def_width, int def_height) 322{ 323#if !defined(SUN4U) 324 struct eeprom *eep = (struct eeprom *)eeprom_va; 325 326 if (!CPU_ISSUN4) { 327 printf("fb_setsize_eeprom: not sun4\n"); 328 return; 329 } 330 331 /* Set up some defaults. */ 332 fb->fb_type.fb_width = def_width; 333 fb->fb_type.fb_height = def_height; 334 335 if (fb->fb_flags & FB_PFOUR) { 336#if NPFOUR > 0 337 fb_setsize_pfour(fb); 338#endif 339 } else if (eep != NULL) { 340 switch (eep->eeScreenSize) { 341 case EE_SCR_1152X900: 342 fb->fb_type.fb_width = 1152; 343 fb->fb_type.fb_height = 900; 344 break; 345 346 case EE_SCR_1024X1024: 347 fb->fb_type.fb_width = 1024; 348 fb->fb_type.fb_height = 1024; 349 break; 350 351 case EE_SCR_1600X1280: 352 fb->fb_type.fb_width = 1600; 353 fb->fb_type.fb_height = 1280; 354 break; 355 356 case EE_SCR_1440X1440: 357 fb->fb_type.fb_width = 1440; 358 fb->fb_type.fb_height = 1440; 359 break; 360 361 default: 362 /* 363 * XXX: Do nothing, I guess. 364 * Should we print a warning about 365 * an unknown value? --thorpej 366 */ 367 break; 368 } 369 } 370 371 fb->fb_linebytes = (fb->fb_type.fb_width * depth) / 8; 372#endif /* !SUN4U */ 373} 374 375 376 377#ifdef RASTERCONSOLE 378static void fb_bell(int); 379 380static void 381fb_bell(int on) 382{ 383#if NKBD > 0 384 kbd_bell(on); 385#endif 386} 387 388void 389fbrcons_init(struct fbdevice *fb) 390{ 391 struct rconsole *rc = &fb->fb_rcons; 392 struct rasops_info *ri = &fb->fb_rinfo; 393 int maxrow, maxcol; 394#if !defined(RASTERCONS_FULLSCREEN) 395 int *row, *col; 396#endif 397 398 /* Set up what rasops needs to know about */ 399 memset(ri, 0, sizeof *ri); 400 ri->ri_stride = fb->fb_linebytes; 401 ri->ri_bits = (void *)fb->fb_pixels; 402 ri->ri_depth = fb->fb_type.fb_depth; 403 ri->ri_width = fb->fb_type.fb_width; 404 ri->ri_height = fb->fb_type.fb_height; 405 maxrow = 5000; 406 maxcol = 5000; 407 408#if !defined(RASTERCONS_FULLSCREEN) 409#if !defined(SUN4U) 410 if (CPU_ISSUN4) { 411 struct eeprom *eep = (struct eeprom *)eeprom_va; 412 413 if (eep == NULL) { 414 maxcol = 80; 415 maxrow = 34; 416 } else { 417 maxcol = eep->eeTtyCols; 418 maxrow = eep->eeTtyRows; 419 } 420 } 421#endif /* !SUN4U */ 422 if (!CPU_ISSUN4) { 423 char buf[6+1]; /* Enough for six digits */ 424 maxcol = (prom_getoption("screen-#columns", buf, sizeof buf) == 0) 425 ? strtoul(buf, NULL, 10) 426 : 80; 427 428 maxrow = (prom_getoption("screen-#rows", buf, sizeof buf) != 0) 429 ? strtoul(buf, NULL, 10) 430 : 34; 431 432 } 433#endif /* !RASTERCONS_FULLSCREEN */ 434 /* 435 * - force monochrome output 436 * - eraserows() hack to clear the *entire* display 437 * - cursor is currently enabled 438 * - center output 439 */ 440 ri->ri_flg = RI_FULLCLEAR | RI_CURSOR | RI_CENTER; 441 442 /* Get operations set and connect to rcons */ 443 if (rasops_init(ri, maxrow, maxcol)) 444 panic("fbrcons_init: rasops_init failed!"); 445 446 if (ri->ri_depth == 8) { 447 int i; 448 for (i = 0; i < 16; i++) { 449 450 /* 451 * Cmap entries are repeated four times in the 452 * 32 bit wide `devcmap' entries for optimization 453 * purposes; see rasops(9) 454 */ 455#define I_TO_DEVCMAP(i) ((i) | ((i)<<8) | ((i)<<16) | ((i)<<24)) 456 457 /* 458 * Use existing colormap entries for black and white 459 */ 460 if ((i & 7) == WSCOL_BLACK) { 461 ri->ri_devcmap[i] = I_TO_DEVCMAP(255); 462 continue; 463 } 464 465 if ((i & 7) == WSCOL_WHITE) { 466 ri->ri_devcmap[i] = I_TO_DEVCMAP(0); 467 continue; 468 } 469 /* 470 * Other entries refer to ANSI map, which for now 471 * is setup in bt_subr.c 472 */ 473 ri->ri_devcmap[i] = I_TO_DEVCMAP(i + 1); 474#undef I_TO_DEVCMAP 475 } 476 } 477 478 rc->rc_row = rc->rc_col = 0; 479#if !defined(RASTERCONS_FULLSCREEN) 480 /* Determine addresses of prom emulator row and column */ 481 if (!CPU_ISSUN4 && !romgetcursoraddr(&row, &col)) { 482 rc->rc_row = *row; 483 rc->rc_col = *col; 484 } 485#endif 486 ri->ri_crow = rc->rc_row; 487 ri->ri_ccol = rc->rc_col; 488 489 rc->rc_ops = &ri->ri_ops; 490 rc->rc_cookie = ri; 491 rc->rc_bell = fb_bell; 492 rc->rc_maxcol = ri->ri_cols; 493 rc->rc_maxrow = ri->ri_rows; 494 rc->rc_width = ri->ri_emuwidth; 495 rc->rc_height = ri->ri_emuheight; 496 rc->rc_deffgcolor = WSCOL_BLACK; 497 rc->rc_defbgcolor = WSCOL_WHITE; 498 rcons_init(rc, 0); 499 500 /* Hook up virtual console */ 501 v_putc = rcons_cnputc; 502} 503 504int 505fbrcons_rows(void) 506{ 507 return ((fblist.fb_dev != NULL) ? 508 fblist.fb_dev->fb_rcons.rc_maxrow : 0); 509} 510 511int 512fbrcons_cols(void) 513{ 514 return ((fblist.fb_dev != NULL) ? 515 fblist.fb_dev->fb_rcons.rc_maxcol : 0); 516} 517#endif /* RASTERCONSOLE */ 518