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