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