xref: /netbsd-src/sys/dev/sun/fb.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: fb.c,v 1.34 2014/03/16 05:20:29 dholland 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.34 2014/03/16 05:20:29 dholland 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 	.d_open = fbopen,
91 	.d_close = fbclose,
92 	.d_read = noread,
93 	.d_write = nowrite,
94 	.d_ioctl = fbioctl,
95 	.d_stop = nostop,
96 	.d_tty = notty,
97 	.d_poll = fbpoll,
98 	.d_mmap = fbmmap,
99 	.d_kqfilter = fbkqfilter,
100 	.d_flag = D_OTHER
101 };
102 
103 void
104 fb_unblank(void)
105 {
106 
107 	struct fbdevlist *fbl = &fblist;
108 
109 	while (fbl != NULL && fbl->fb_dev != NULL) {
110 		(*fbl->fb_dev->fb_driver->fbd_unblank)(fbl->fb_dev->fb_device);
111 		fbl = fbl->fb_next;
112 	}
113 }
114 
115 /*
116  * Helper function for frame buffer devices. Decides whether
117  * the device can be the console output device according to
118  * PROM info. The result from this function may not be conclusive
119  * on machines with old PROMs; in that case, drivers should consult
120  * other sources of configuration information (e.g. EEPROM entries).
121  */
122 int
123 fb_is_console(int node)
124 {
125 #if !defined(SUN4U)
126 	int fbnode;
127 
128 	switch (prom_version()) {
129 	case PROM_OLDMON:
130 		/* `node' is not valid; just check for any fb device */
131 		return (prom_stdout() == PROMDEV_SCREEN);
132 
133 	case PROM_OBP_V0:
134 		/*
135 		 * First, check if prom_stdout() represents a frame buffer,
136 		 * then match on the `fb' property on the root node, if any.
137 		 */
138 		if (prom_stdout() != PROMDEV_SCREEN)
139 			return (0);
140 
141 		fbnode = prom_getpropint(findroot(), "fb", 0);
142 		return (fbnode == 0 || node == fbnode);
143 
144 	case PROM_OBP_V2:
145 	case PROM_OBP_V3:
146 	case PROM_OPENFIRM:
147 		/* Just match the nodes */
148 		return (node == prom_stdout_node);
149 	}
150 
151 	return (0);
152 #else
153 		return (node == prom_stdout_node);
154 #endif
155 }
156 
157 void
158 fb_attach(struct fbdevice *fb, int isconsole)
159 {
160 	static int seen_force = 0;
161 	int nfb = 0;
162 	struct fbdevlist *fbl = &fblist;
163 
164 	/*
165 	 * Check to see if we're being forced into /dev/fb0, or if we're
166 	 * the console.  If we are, then move/replace the current fb0.
167 	 */
168 	if ((fb->fb_flags & FB_FORCE || (isconsole && !seen_force)) &&
169 	    fblist.fb_dev != NULL) {
170 		while (fbl->fb_next != NULL) {
171 			fbl = fbl->fb_next;
172 			nfb++;
173 		}
174 		if ((fbl->fb_next = malloc(sizeof (struct fbdevlist),
175 		    M_DEVBUF, M_NOWAIT)) == NULL)
176 			printf("%s: replacing %s at /dev/fb0\n",
177 			    device_xname(fb->fb_device),
178 			    device_xname(fblist.fb_dev->fb_device));
179 		else {
180 			fbl = fbl->fb_next;
181 			nfb++;
182 			fbl->fb_dev = fblist.fb_dev;
183 			fbl->fb_next = NULL;
184 			aprint_normal_dev(fbl->fb_dev->fb_device,
185 			    "moved to /dev/fb%d\n", nfb);
186 			aprint_normal_dev(fbl->fb_dev->fb_device,
187 			    "attached to /dev/fb0\n");
188 		}
189 		fblist.fb_dev = fb;
190 		if (fb->fb_flags & FB_FORCE)
191 			seen_force = 1;
192 	/* Add to end of fb list. */
193 	} else {
194 		if (fblist.fb_dev != NULL) {
195 			while (fbl->fb_next != NULL) {
196 				fbl = fbl->fb_next;
197 				nfb++;
198 			}
199 			if ((fbl->fb_next = malloc(sizeof (struct fbdevlist),
200 			    M_DEVBUF, M_NOWAIT)) == NULL) {
201 				aprint_error_dev(fb->fb_device,
202 				    "no space to attach after /dev/fb%d\n",
203 				    nfb);
204 				return;
205 			}
206 			fbl = fbl->fb_next;
207 			nfb++;
208 		}
209 		fbl->fb_dev = fb;
210 		fbl->fb_next = NULL;
211 		aprint_normal_dev(fbl->fb_dev->fb_device,
212 		     "attached to /dev/fb%d\n", nfb);
213 	}
214 }
215 
216 int
217 fbopen(dev_t dev, int flags, int mode, struct lwp *l)
218 {
219 	int unit, nunit;
220 	struct fbdevlist *fbl = &fblist;
221 
222 	unit = minor(dev);
223 	while (unit-- && fbl != NULL)
224 		fbl = fbl->fb_next;
225 	if (fbl == NULL || fbl->fb_dev == NULL)
226 		return (ENXIO);
227 
228 	nunit = device_unit(fbl->fb_dev->fb_device);
229 	return (fbl->fb_dev->fb_driver->fbd_open)(makedev(0, nunit), flags,
230 	    mode, l);
231 }
232 
233 int
234 fbclose(dev_t dev, int flags, int mode, struct lwp *l)
235 {
236 	int unit, nunit;
237 	struct fbdevlist *fbl = &fblist;
238 
239 	unit = minor(dev);
240 	while (unit-- && fbl != NULL)
241 		fbl = fbl->fb_next;
242 	if (fbl == NULL || fbl->fb_dev == NULL)
243 		return (ENXIO);
244 
245 	nunit = device_unit(fbl->fb_dev->fb_device);
246 	return (fbl->fb_dev->fb_driver->fbd_close)(makedev(0, nunit), flags,
247 	    mode, l);
248 }
249 
250 int
251 fbioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
252 {
253 	int unit, nunit;
254 	struct fbdevlist *fbl = &fblist;
255 
256 	unit = minor(dev);
257 	while (unit-- && fbl != NULL)
258 		fbl = fbl->fb_next;
259 	if (fbl == NULL || fbl->fb_dev == NULL)
260 		return (ENXIO);
261 
262 	nunit = device_unit(fbl->fb_dev->fb_device);
263 	return (fbl->fb_dev->fb_driver->fbd_ioctl)(makedev(0, nunit), cmd,
264 	    data, flags, l);
265 }
266 
267 int
268 fbpoll(dev_t dev, int events, struct lwp *l)
269 {
270 	int unit, nunit;
271 	struct fbdevlist *fbl = &fblist;
272 
273 	unit = minor(dev);
274 	while (unit-- && fbl != NULL)
275 		fbl = fbl->fb_next;
276 	if (fbl == NULL || fbl->fb_dev == NULL)
277 		return (ENXIO);
278 
279 	nunit = device_unit(fbl->fb_dev->fb_device);
280 	return (fbl->fb_dev->fb_driver->fbd_poll)(makedev(0, nunit), events,
281 	    l);
282 }
283 
284 int
285 fbkqfilter(dev_t dev, struct knote *kn)
286 {
287 	int unit, nunit;
288 	struct fbdevlist *fbl = &fblist;
289 
290 	unit = minor(dev);
291 	while (unit-- && fbl != NULL)
292 		fbl = fbl->fb_next;
293 	if (fbl == NULL || fbl->fb_dev == NULL)
294 		return (ENXIO);
295 
296 	nunit = device_unit(fbl->fb_dev->fb_device);
297 	return (fbl->fb_dev->fb_driver->fbd_kqfilter)(makedev(0, nunit), kn);
298 }
299 
300 paddr_t
301 fbmmap(dev_t dev, off_t off, int prot)
302 {
303 	int unit, nunit;
304 	struct fbdevlist *fbl = &fblist;
305 
306 	unit = minor(dev);
307 	while (unit-- && fbl != NULL)
308 		fbl = fbl->fb_next;
309 	if (fbl == NULL || fbl->fb_dev == NULL)
310 		return (ENXIO);
311 
312 	nunit = device_unit(fbl->fb_dev->fb_device);
313 	paddr_t (*map)(dev_t, off_t, int) = fbl->fb_dev->fb_driver->fbd_mmap;
314 
315 	if (map == NULL)
316 		return (-1);
317 	return (map(makedev(0, nunit), off, prot));
318 }
319 
320 void
321 fb_setsize_obp(struct fbdevice *fb, int depth, int def_width, int def_height, int node)
322 {
323 	fb->fb_type.fb_width = prom_getpropint(node, "width", def_width);
324 	fb->fb_type.fb_height = prom_getpropint(node, "height", def_height);
325 	fb->fb_linebytes = prom_getpropint(node, "linebytes",
326 				     (fb->fb_type.fb_width * depth) / 8);
327 }
328 
329 void
330 fb_setsize_eeprom(struct fbdevice *fb, int depth, int def_width, int def_height)
331 {
332 #if !defined(SUN4U)
333 	struct eeprom *eep = (struct eeprom *)eeprom_va;
334 
335 	if (!CPU_ISSUN4) {
336 		printf("fb_setsize_eeprom: not sun4\n");
337 		return;
338 	}
339 
340 	/* Set up some defaults. */
341 	fb->fb_type.fb_width = def_width;
342 	fb->fb_type.fb_height = def_height;
343 
344 	if (fb->fb_flags & FB_PFOUR) {
345 #if NPFOUR > 0
346 		fb_setsize_pfour(fb);
347 #endif
348 	} else if (eep != NULL) {
349 		switch (eep->eeScreenSize) {
350 		case EE_SCR_1152X900:
351 			fb->fb_type.fb_width = 1152;
352 			fb->fb_type.fb_height = 900;
353 			break;
354 
355 		case EE_SCR_1024X1024:
356 			fb->fb_type.fb_width = 1024;
357 			fb->fb_type.fb_height = 1024;
358 			break;
359 
360 		case EE_SCR_1600X1280:
361 			fb->fb_type.fb_width = 1600;
362 			fb->fb_type.fb_height = 1280;
363 			break;
364 
365 		case EE_SCR_1440X1440:
366 			fb->fb_type.fb_width = 1440;
367 			fb->fb_type.fb_height = 1440;
368 			break;
369 
370 		default:
371 			/*
372 			 * XXX: Do nothing, I guess.
373 			 * Should we print a warning about
374 			 * an unknown value? --thorpej
375 			 */
376 			break;
377 		}
378 	}
379 
380 	fb->fb_linebytes = (fb->fb_type.fb_width * depth) / 8;
381 #endif /* !SUN4U */
382 }
383 
384 
385 
386 #ifdef RASTERCONSOLE
387 static void fb_bell(int);
388 
389 static void
390 fb_bell(int on)
391 {
392 #if NKBD > 0
393 	kbd_bell(on);
394 #endif
395 }
396 
397 void
398 fbrcons_init(struct fbdevice *fb)
399 {
400 	struct rconsole	*rc = &fb->fb_rcons;
401 	struct rasops_info *ri = &fb->fb_rinfo;
402 	int maxrow, maxcol;
403 #if !defined(RASTERCONS_FULLSCREEN)
404 	int *row, *col;
405 #endif
406 
407 	/* Set up what rasops needs to know about */
408 	memset(ri, 0, sizeof *ri);
409 	ri->ri_stride = fb->fb_linebytes;
410 	ri->ri_bits = (void *)fb->fb_pixels;
411 	ri->ri_depth = fb->fb_type.fb_depth;
412 	ri->ri_width = fb->fb_type.fb_width;
413 	ri->ri_height = fb->fb_type.fb_height;
414 	maxrow = 5000;
415 	maxcol = 5000;
416 
417 #if !defined(RASTERCONS_FULLSCREEN)
418 #if !defined(SUN4U)
419 	if (CPU_ISSUN4) {
420 		struct eeprom *eep = (struct eeprom *)eeprom_va;
421 
422 		if (eep == NULL) {
423 			maxcol = 80;
424 			maxrow = 34;
425 		} else {
426 			maxcol = eep->eeTtyCols;
427 			maxrow = eep->eeTtyRows;
428 		}
429 	}
430 #endif /* !SUN4U */
431 	if (!CPU_ISSUN4) {
432 		char buf[6+1];	/* Enough for six digits */
433 		maxcol = (prom_getoption("screen-#columns", buf, sizeof buf) == 0)
434 			? strtoul(buf, NULL, 10)
435 			: 80;
436 
437 		maxrow = (prom_getoption("screen-#rows", buf, sizeof buf) != 0)
438 			? strtoul(buf, NULL, 10)
439 			: 34;
440 
441 	}
442 #endif /* !RASTERCONS_FULLSCREEN */
443 	/*
444 	 * - force monochrome output
445 	 * - eraserows() hack to clear the *entire* display
446 	 * - cursor is currently enabled
447 	 * - center output
448 	 */
449 	ri->ri_flg = RI_FULLCLEAR | RI_CURSOR | RI_CENTER;
450 
451 	/* Get operations set and connect to rcons */
452 	if (rasops_init(ri, maxrow, maxcol))
453 		panic("fbrcons_init: rasops_init failed!");
454 
455 	if (ri->ri_depth == 8) {
456 		int i;
457 		for (i = 0; i < 16; i++) {
458 
459 			/*
460 			 * Cmap entries are repeated four times in the
461 			 * 32 bit wide `devcmap' entries for optimization
462 			 * purposes; see rasops(9)
463 			 */
464 #define I_TO_DEVCMAP(i)	((i) | ((i)<<8) | ((i)<<16) | ((i)<<24))
465 
466 			/*
467 			 * Use existing colormap entries for black and white
468 			 */
469 			if ((i & 7) == WSCOL_BLACK) {
470 				ri->ri_devcmap[i] = I_TO_DEVCMAP(255);
471 				continue;
472 			}
473 
474 			if ((i & 7) == WSCOL_WHITE) {
475 				ri->ri_devcmap[i] = I_TO_DEVCMAP(0);
476 				continue;
477 			}
478 			/*
479 			 * Other entries refer to ANSI map, which for now
480 			 * is setup in bt_subr.c
481 			 */
482 			ri->ri_devcmap[i] = I_TO_DEVCMAP(i + 1);
483 #undef I_TO_DEVCMAP
484 		}
485 	}
486 
487 	rc->rc_row = rc->rc_col = 0;
488 #if !defined(RASTERCONS_FULLSCREEN)
489 	/* Determine addresses of prom emulator row and column */
490 	if (!CPU_ISSUN4 && !romgetcursoraddr(&row, &col)) {
491 		rc->rc_row = *row;
492 		rc->rc_col = *col;
493 	}
494 #endif
495 	ri->ri_crow = rc->rc_row;
496 	ri->ri_ccol = rc->rc_col;
497 
498 	rc->rc_ops = &ri->ri_ops;
499 	rc->rc_cookie = ri;
500 	rc->rc_bell = fb_bell;
501 	rc->rc_maxcol = ri->ri_cols;
502 	rc->rc_maxrow = ri->ri_rows;
503 	rc->rc_width = ri->ri_emuwidth;
504 	rc->rc_height = ri->ri_emuheight;
505 	rc->rc_deffgcolor = WSCOL_BLACK;
506 	rc->rc_defbgcolor = WSCOL_WHITE;
507 	rcons_init(rc, 0);
508 
509 	/* Hook up virtual console */
510 	v_putc = rcons_cnputc;
511 }
512 
513 int
514 fbrcons_rows(void)
515 {
516 	return ((fblist.fb_dev != NULL) ?
517 	    fblist.fb_dev->fb_rcons.rc_maxrow : 0);
518 }
519 
520 int
521 fbrcons_cols(void)
522 {
523 	return ((fblist.fb_dev != NULL) ?
524 	    fblist.fb_dev->fb_rcons.rc_maxcol : 0);
525 }
526 #endif /* RASTERCONSOLE */
527