xref: /original-bsd/sys/sparc/sbus/cgsix.c (revision 333da485)
1 /*
2  * Copyright (c) 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This software was developed by the Computer Systems Engineering group
6  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
7  * contributed to Berkeley.
8  *
9  * All advertising materials mentioning features or use of this software
10  * must display the following acknowledgement:
11  *	This product includes software developed by the University of
12  *	California, Lawrence Berkeley Laboratory.
13  *
14  * %sccs.include.redist.c%
15  *
16  *	@(#)cgsix.c	8.4 (Berkeley) 01/21/94
17  *
18  * from: $Header: cgsix.c,v 1.2 93/10/18 00:01:51 torek Exp $
19  */
20 
21 /*
22  * color display (cgsix) driver.
23  *
24  * Does not handle interrupts, even though they can occur.
25  *
26  * XXX should defer colormap updates to vertical retrace interrupts
27  */
28 
29 #include <sys/param.h>
30 #include <sys/buf.h>
31 #include <sys/device.h>
32 #include <sys/fbio.h>
33 #include <sys/ioctl.h>
34 #include <sys/malloc.h>
35 #include <sys/mman.h>
36 #include <sys/tty.h>
37 
38 #ifdef DEBUG
39 #include <sys/proc.h>
40 #include <sys/syslog.h>
41 #endif
42 
43 #include <machine/autoconf.h>
44 #include <machine/pmap.h>
45 #include <machine/fbvar.h>
46 
47 #include <sparc/sbus/btreg.h>
48 #include <sparc/sbus/btvar.h>
49 #include <sparc/sbus/cgsixreg.h>
50 #include <sparc/sbus/sbusvar.h>
51 
52 union cursor_cmap {		/* colormap, like bt_cmap, but tiny */
53 	u_char	cm_map[2][3];	/* 2 R/G/B entries */
54 	u_int	cm_chip[2];	/* 2 chip equivalents */
55 };
56 
57 struct cg6_cursor {		/* cg6 hardware cursor status */
58 	short	cc_enable;		/* cursor is enabled */
59 	struct	fbcurpos cc_pos;	/* position */
60 	struct	fbcurpos cc_hot;	/* hot-spot */
61 	struct	fbcurpos cc_size;	/* size of mask & image fields */
62 	u_int	cc_bits[2][32];		/* space for mask & image bits */
63 	union	cursor_cmap cc_color;	/* cursor colormap */
64 };
65 
66 /* per-display variables */
67 struct cgsix_softc {
68 	struct	device sc_dev;		/* base device */
69 	struct	sbusdev sc_sd;		/* sbus device */
70 	struct	fbdevice sc_fb;		/* frame buffer device */
71 	volatile struct cg6_layout *sc_physadr;	/* phys addr of h/w */
72 	volatile struct bt_regs *sc_bt;		/* Brooktree registers */
73 	volatile int *sc_fhc;			/* FHC register */
74 	volatile struct cg6_thc *sc_thc;	/* THC registers */
75 	volatile struct cg6_tec_xxx *sc_tec;	/* TEC registers */
76 	short	sc_fhcrev;		/* hardware rev */
77 	short	sc_blanked;		/* true if blanked */
78 	struct	cg6_cursor sc_cursor;	/* software cursor info */
79 	union	bt_cmap sc_cmap;	/* Brooktree color map */
80 };
81 
82 /* autoconfiguration driver */
83 static void	cgsixattach(struct device *, struct device *, void *);
84 struct cfdriver cgsixcd =
85     { NULL, "cgsix", matchbyname, cgsixattach,
86       DV_DULL, sizeof(struct cgsix_softc) };
87 
88 /* frame buffer generic driver */
89 static void	cg6_unblank(struct device *);
90 static struct fbdriver cg6_fbdriver = { cg6_unblank };
91 
92 /*
93  * Unlike the bw2 and cg3 drivers, we do not need to provide an rconsole
94  * interface, as the cg6 is fast enough.
95  */
96 
97 extern int fbnode;
98 
99 #define	CGSIX_MAJOR	67		/* XXX */
100 
101 static void cg6_reset __P((struct cgsix_softc *));
102 static void cg6_loadcmap __P((struct cgsix_softc *, int, int));
103 static void cg6_loadomap __P((struct cgsix_softc *));
104 static void cg6_setcursor __P((struct cgsix_softc *));/* set position */
105 static void cg6_loadcursor __P((struct cgsix_softc *));/* set shape */
106 
107 /*
108  * Attach a display.
109  */
110 void
111 cgsixattach(parent, self, args)
112 	struct device *parent, *self;
113 	void *args;
114 {
115 	register struct cgsix_softc *sc = (struct cgsix_softc *)self;
116 	register struct sbus_attach_args *sa = args;
117 	register int node = sa->sa_ra.ra_node, ramsize, i;
118 	register volatile struct bt_regs *bt;
119 	register volatile struct cg6_layout *p;
120 
121 	sc->sc_fb.fb_major = CGSIX_MAJOR;	/* XXX to be removed */
122 
123 	sc->sc_fb.fb_driver = &cg6_fbdriver;
124 	sc->sc_fb.fb_device = &sc->sc_dev;
125 	sc->sc_fb.fb_type.fb_type = FBTYPE_SUNFAST_COLOR;
126 	sc->sc_fb.fb_type.fb_width = getpropint(node, "width", 1152);
127 	sc->sc_fb.fb_type.fb_height = getpropint(node, "height", 900);
128 	sc->sc_fb.fb_linebytes = getpropint(node, "linebytes", 1152);
129 	ramsize = sc->sc_fb.fb_type.fb_height * sc->sc_fb.fb_linebytes;
130 	sc->sc_fb.fb_type.fb_depth = 8;
131 	sc->sc_fb.fb_type.fb_cmsize = 256;
132 	sc->sc_fb.fb_type.fb_size = ramsize;
133 	printf(": %s, %d x %d", getpropstring(node, "model"),
134 	    sc->sc_fb.fb_type.fb_width, sc->sc_fb.fb_type.fb_height);
135 
136 	/*
137 	 * Dunno what the PROM has mapped, though obviously it must have
138 	 * the video RAM mapped.  Just map what we care about for ourselves
139 	 * (the FHC, THC, and Brooktree registers).
140 	 */
141 	sc->sc_physadr = p = (struct cg6_layout *)sa->sa_ra.ra_paddr;
142 	sc->sc_bt = bt = (volatile struct bt_regs *)
143 	    mapiodev((caddr_t)&p->cg6_bt_un.un_btregs, sizeof *sc->sc_bt);
144 	sc->sc_fhc = (volatile int *)
145 	    mapiodev((caddr_t)&p->cg6_fhc_un.un_fhc, sizeof *sc->sc_fhc);
146 	sc->sc_thc = (volatile struct cg6_thc *)
147 	    mapiodev((caddr_t)&p->cg6_thc_un.un_thc, sizeof *sc->sc_thc);
148 	sc->sc_tec = (volatile struct cg6_tec_xxx *)
149 	    mapiodev((caddr_t)&p->cg6_tec_un.un_tec, sizeof *sc->sc_tec);
150 
151 	sc->sc_fhcrev = (*sc->sc_fhc >> FHC_REV_SHIFT) &
152 	    (FHC_REV_MASK >> FHC_REV_SHIFT);
153 	printf(", rev %d", sc->sc_fhcrev);
154 
155 	/* reset cursor & frame buffer controls */
156 	cg6_reset(sc);
157 
158 	/* grab initial (current) color map (DOES THIS WORK?) */
159 	bt->bt_addr = 0;
160 	for (i = 0; i < 256 * 3; i++)
161 		((char *)&sc->sc_cmap)[i] = bt->bt_cmap >> 24;
162 
163 	/* enable video */
164 	sc->sc_thc->thc_misc |= THC_MISC_VIDEN;
165 
166 	printf("\n");
167 	sbus_establish(&sc->sc_sd, &sc->sc_dev);
168 	if (node == fbnode)
169 		fb_attach(&sc->sc_fb);
170 }
171 
172 int
173 cgsixopen(dev, flags, mode, p)
174 	dev_t dev;
175 	int flags, mode;
176 	struct proc *p;
177 {
178 	int unit = minor(dev);
179 
180 	if (unit >= cgsixcd.cd_ndevs || cgsixcd.cd_devs[unit] == NULL)
181 		return (ENXIO);
182 	return (0);
183 }
184 
185 int
186 cgsixclose(dev, flags, mode, p)
187 	dev_t dev;
188 	int flags, mode;
189 	struct proc *p;
190 {
191 	struct cgsix_softc *sc = cgsixcd.cd_devs[minor(dev)];
192 
193 	cg6_reset(sc);
194 	return (0);
195 }
196 
197 int
198 cgsixioctl(dev, cmd, data, flags, p)
199 	dev_t dev;
200 	int cmd;
201 	register caddr_t data;
202 	int flags;
203 	struct proc *p;
204 {
205 	register struct cgsix_softc *sc = cgsixcd.cd_devs[minor(dev)];
206 	u_int count;
207 	int i, v, error;
208 	union cursor_cmap tcm;
209 
210 	switch (cmd) {
211 
212 	case FBIOGTYPE:
213 		*(struct fbtype *)data = sc->sc_fb.fb_type;
214 		break;
215 
216 	case FBIOGATTR:
217 #define fba ((struct fbgattr *)data)
218 		fba->real_type = sc->sc_fb.fb_type.fb_type;
219 		fba->owner = 0;		/* XXX ??? */
220 		fba->fbtype = sc->sc_fb.fb_type;
221 		fba->sattr.flags = 0;
222 		fba->sattr.emu_type = sc->sc_fb.fb_type.fb_type;
223 		fba->sattr.dev_specific[0] = -1;
224 		fba->emu_types[0] = sc->sc_fb.fb_type.fb_type;
225 		fba->emu_types[1] = -1;
226 #undef fba
227 		break;
228 
229 	case FBIOGETCMAP:
230 		return (bt_getcmap((struct fbcmap *)data, &sc->sc_cmap, 256));
231 
232 	case FBIOPUTCMAP:
233 		/* copy to software map */
234 #define	p ((struct fbcmap *)data)
235 		error = bt_putcmap(p, &sc->sc_cmap, 256);
236 		if (error)
237 			return (error);
238 		/* now blast them into the chip */
239 		/* XXX should use retrace interrupt */
240 		cg6_loadcmap(sc, p->index, p->count);
241 #undef p
242 		break;
243 
244 	case FBIOGVIDEO:
245 		*(int *)data = sc->sc_blanked;
246 		break;
247 
248 	case FBIOSVIDEO:
249 		if (*(int *)data)
250 			cg6_unblank(&sc->sc_dev);
251 		else if (!sc->sc_blanked) {
252 			sc->sc_blanked = 1;
253 			sc->sc_thc->thc_misc &= ~THC_MISC_VIDEN;
254 		}
255 		break;
256 
257 /* these are for both FBIOSCURSOR and FBIOGCURSOR */
258 #define p ((struct fbcursor *)data)
259 #define cc (&sc->sc_cursor)
260 
261 	case FBIOGCURSOR:
262 		/* do not quite want everything here... */
263 		p->set = FB_CUR_SETALL;	/* close enough, anyway */
264 		p->enable = cc->cc_enable;
265 		p->pos = cc->cc_pos;
266 		p->hot = cc->cc_hot;
267 		p->size = cc->cc_size;
268 
269 		/* begin ugh ... can we lose some of this crap?? */
270 		if (p->image != NULL) {
271 			count = cc->cc_size.y * 32 / NBBY;
272 			error = copyout((caddr_t)cc->cc_bits[1],
273 			    (caddr_t)p->image, count);
274 			if (error)
275 				return (error);
276 			error = copyout((caddr_t)cc->cc_bits[0],
277 			    (caddr_t)p->mask, count);
278 			if (error)
279 				return (error);
280 		}
281 		if (p->cmap.red != NULL) {
282 			error = bt_getcmap(&p->cmap,
283 			    (union bt_cmap *)&cc->cc_color, 2);
284 			if (error)
285 				return (error);
286 		} else {
287 			p->cmap.index = 0;
288 			p->cmap.count = 2;
289 		}
290 		/* end ugh */
291 		break;
292 
293 	case FBIOSCURSOR:
294 		/*
295 		 * For setcmap and setshape, verify parameters, so that
296 		 * we do not get halfway through an update and then crap
297 		 * out with the software state screwed up.
298 		 */
299 		v = p->set;
300 		if (v & FB_CUR_SETCMAP) {
301 			/*
302 			 * This use of a temporary copy of the cursor
303 			 * colormap is not terribly efficient, but these
304 			 * copies are small (8 bytes)...
305 			 */
306 			tcm = cc->cc_color;
307 			error = bt_putcmap(&p->cmap, (union bt_cmap *)&tcm, 2);
308 			if (error)
309 				return (error);
310 		}
311 		if (v & FB_CUR_SETSHAPE) {
312 			if ((u_int)p->size.x > 32 || (u_int)p->size.y > 32)
313 				return (EINVAL);
314 			count = p->size.y * 32 / NBBY;
315 			if (!useracc(p->image, count, B_READ) ||
316 			    !useracc(p->mask, count, B_READ))
317 				return (EFAULT);
318 		}
319 
320 		/* parameters are OK; do it */
321 		if (v & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)) {
322 			if (v & FB_CUR_SETCUR)
323 				cc->cc_enable = p->enable;
324 			if (v & FB_CUR_SETPOS)
325 				cc->cc_pos = p->pos;
326 			if (v & FB_CUR_SETHOT)
327 				cc->cc_hot = p->hot;
328 			cg6_setcursor(sc);
329 		}
330 		if (v & FB_CUR_SETCMAP) {
331 			cc->cc_color = tcm;
332 			cg6_loadomap(sc); /* XXX defer to vertical retrace */
333 		}
334 		if (v & FB_CUR_SETSHAPE) {
335 			cc->cc_size = p->size;
336 			count = p->size.y * 32 / NBBY;
337 			bzero((caddr_t)cc->cc_bits, sizeof cc->cc_bits);
338 			bcopy(p->mask, (caddr_t)cc->cc_bits[0], count);
339 			bcopy(p->image, (caddr_t)cc->cc_bits[1], count);
340 			cg6_loadcursor(sc);
341 		}
342 		break;
343 
344 #undef p
345 #undef cc
346 
347 	case FBIOGCURPOS:
348 		*(struct fbcurpos *)data = sc->sc_cursor.cc_pos;
349 		break;
350 
351 	case FBIOSCURPOS:
352 		sc->sc_cursor.cc_pos = *(struct fbcurpos *)data;
353 		cg6_setcursor(sc);
354 		break;
355 
356 	case FBIOGCURMAX:
357 		/* max cursor size is 32x32 */
358 		((struct fbcurpos *)data)->x = 32;
359 		((struct fbcurpos *)data)->y = 32;
360 		break;
361 
362 	default:
363 #ifdef DEBUG
364 		log(LOG_NOTICE, "cgsixioctl(%x) (%s[%d])\n", cmd,
365 		    p->p_comm, p->p_pid);
366 #endif
367 		return (ENOTTY);
368 	}
369 	return (0);
370 }
371 
372 /*
373  * Clean up hardware state (e.g., after bootup or after X crashes).
374  */
375 static void
376 cg6_reset(sc)
377 	register struct cgsix_softc *sc;
378 {
379 	register volatile struct cg6_tec_xxx *tec;
380 	register int fhc;
381 	register volatile struct bt_regs *bt;
382 
383 	/* hide the cursor, just in case */
384 	sc->sc_thc->thc_cursxy = (THC_CURSOFF << 16) | THC_CURSOFF;
385 
386 	/* turn off frobs in transform engine (makes X11 work) */
387 	tec = sc->sc_tec;
388 	tec->tec_mv = 0;
389 	tec->tec_clip = 0;
390 	tec->tec_vdc = 0;
391 
392 	/* take care of hardware bugs in old revisions */
393 	if (sc->sc_fhcrev < 5) {
394 		/*
395 		 * Keep current resolution; set cpu to 68020, set test
396 		 * window (size 1Kx1K), and for rev 1, disable dest cache.
397 		 */
398 		fhc = (*sc->sc_fhc & FHC_RES_MASK) | FHC_CPU_68020 |
399 		    FHC_TEST |
400 		    (11 << FHC_TESTX_SHIFT) | (11 << FHC_TESTY_SHIFT);
401 		if (sc->sc_fhcrev < 2)
402 			fhc |= FHC_DST_DISABLE;
403 		*sc->sc_fhc = fhc;
404 	}
405 
406 	/* Enable cursor in Brooktree DAC. */
407 	bt = sc->sc_bt;
408 	bt->bt_addr = 0x06 << 24;
409 	bt->bt_ctrl |= 0x03 << 24;
410 }
411 
412 static void
413 cg6_setcursor(sc)
414 	register struct cgsix_softc *sc;
415 {
416 
417 	/* we need to subtract the hot-spot value here */
418 #define COORD(f) (sc->sc_cursor.cc_pos.f - sc->sc_cursor.cc_hot.f)
419 	sc->sc_thc->thc_cursxy = sc->sc_cursor.cc_enable ?
420 	    ((COORD(x) << 16) | (COORD(y) & 0xffff)) :
421 	    (THC_CURSOFF << 16) | THC_CURSOFF;
422 #undef COORD
423 }
424 
425 static void
426 cg6_loadcursor(sc)
427 	register struct cgsix_softc *sc;
428 {
429 	register volatile struct cg6_thc *thc;
430 	register u_int edgemask, m;
431 	register int i;
432 
433 	/*
434 	 * Keep the top size.x bits.  Here we *throw out* the top
435 	 * size.x bits from an all-one-bits word, introducing zeros in
436 	 * the top size.x bits, then invert all the bits to get what
437 	 * we really wanted as our mask.  But this fails if size.x is
438 	 * 32---a sparc uses only the low 5 bits of the shift count---
439 	 * so we have to special case that.
440 	 */
441 	edgemask = ~0;
442 	if (sc->sc_cursor.cc_size.x < 32)
443 		edgemask = ~(edgemask >> sc->sc_cursor.cc_size.x);
444 	thc = sc->sc_thc;
445 	for (i = 0; i < 32; i++) {
446 		m = sc->sc_cursor.cc_bits[0][i] & edgemask;
447 		thc->thc_cursmask[i] = m;
448 		thc->thc_cursbits[i] = m & sc->sc_cursor.cc_bits[1][i];
449 	}
450 }
451 
452 /*
453  * Load a subset of the current (new) colormap into the color DAC.
454  */
455 static void
456 cg6_loadcmap(sc, start, ncolors)
457 	register struct cgsix_softc *sc;
458 	register int start, ncolors;
459 {
460 	register volatile struct bt_regs *bt;
461 	register u_int *ip, i;
462 	register int count;
463 
464 	ip = &sc->sc_cmap.cm_chip[BT_D4M3(start)];	/* start/4 * 3 */
465 	count = BT_D4M3(start + ncolors - 1) - BT_D4M3(start) + 3;
466 	bt = sc->sc_bt;
467 	bt->bt_addr = BT_D4M4(start) << 24;
468 	while (--count >= 0) {
469 		i = *ip++;
470 		/* hardware that makes one want to pound boards with hammers */
471 		bt->bt_cmap = i;
472 		bt->bt_cmap = i << 8;
473 		bt->bt_cmap = i << 16;
474 		bt->bt_cmap = i << 24;
475 	}
476 }
477 
478 /*
479  * Load the cursor (overlay `foreground' and `background') colors.
480  */
481 static void
482 cg6_loadomap(sc)
483 	register struct cgsix_softc *sc;
484 {
485 	register volatile struct bt_regs *bt;
486 	register u_int i;
487 
488 	bt = sc->sc_bt;
489 	bt->bt_addr = 0x01 << 24;	/* set background color */
490 	i = sc->sc_cursor.cc_color.cm_chip[0];
491 	bt->bt_omap = i;		/* R */
492 	bt->bt_omap = i << 8;		/* G */
493 	bt->bt_omap = i << 16;		/* B */
494 
495 	bt->bt_addr = 0x03 << 24;	/* set foreground color */
496 	bt->bt_omap = i << 24;		/* R */
497 	i = sc->sc_cursor.cc_color.cm_chip[1];
498 	bt->bt_omap = i;		/* G */
499 	bt->bt_omap = i << 8;		/* B */
500 }
501 
502 static void
503 cg6_unblank(dev)
504 	struct device *dev;
505 {
506 	struct cgsix_softc *sc = (struct cgsix_softc *)dev;
507 
508 	if (sc->sc_blanked) {
509 		sc->sc_blanked = 0;
510 		sc->sc_thc->thc_misc |= THC_MISC_VIDEN;
511 	}
512 }
513 
514 /* XXX the following should be moved to a "user interface" header */
515 /*
516  * Base addresses at which users can mmap() the various pieces of a cg6.
517  * Note that although the Brooktree color registers do not occupy 8K,
518  * the X server dies if we do not allow it to map 8K there (it just maps
519  * from 0x70000000 forwards, as a contiguous chunk).
520  */
521 #define	CG6_USER_FBC	0x70000000
522 #define	CG6_USER_TEC	0x70001000
523 #define	CG6_USER_BTREGS	0x70002000
524 #define	CG6_USER_FHC	0x70004000
525 #define	CG6_USER_THC	0x70005000
526 #define	CG6_USER_ROM	0x70006000
527 #define	CG6_USER_RAM	0x70016000
528 #define	CG6_USER_DHC	0x80000000
529 
530 struct mmo {
531 	u_int	mo_uaddr;	/* user (virtual) address */
532 	u_int	mo_size;	/* size, or 0 for video ram size */
533 	u_int	mo_physoff;	/* offset from sc_physadr */
534 };
535 
536 /*
537  * Return the address that would map the given device at the given
538  * offset, allowing for the given protection, or return -1 for error.
539  *
540  * XXX	needs testing against `demanding' applications (e.g., aviator)
541  */
542 int
543 cgsixmap(dev, off, prot)
544 	dev_t dev;
545 	int off, prot;
546 {
547 	register struct cgsix_softc *sc = cgsixcd.cd_devs[minor(dev)];
548 	register struct mmo *mo;
549 	register u_int u, sz;
550 #define	O(memb) ((u_int)(&((struct cg6_layout *)0)->memb))
551 	static struct mmo mmo[] = {
552 		{ CG6_USER_RAM, 0, O(cg6_ram) },
553 
554 		/* do not actually know how big most of these are! */
555 		{ CG6_USER_FBC, 1, O(cg6_fbc_un) },
556 		{ CG6_USER_TEC, 1, O(cg6_tec_un) },
557 		{ CG6_USER_BTREGS, 8192 /* XXX */, O(cg6_bt_un) },
558 		{ CG6_USER_FHC, 1, O(cg6_fhc_un) },
559 		{ CG6_USER_THC, sizeof(struct cg6_thc), O(cg6_thc_un) },
560 		{ CG6_USER_ROM, 65536, O(cg6_rom_un) },
561 		{ CG6_USER_DHC, 1, O(cg6_dhc_un) },
562 	};
563 #define NMMO (sizeof mmo / sizeof *mmo)
564 
565 	if (off & PGOFSET)
566 		panic("cgsixmap");
567 
568 	/*
569 	 * Entries with size 0 map video RAM (i.e., the size in fb data).
570 	 *
571 	 * Since we work in pages, the fact that the map offset table's
572 	 * sizes are sometimes bizarre (e.g., 1) is effectively ignored:
573 	 * one byte is as good as one page.
574 	 */
575 	for (mo = mmo; mo < &mmo[NMMO]; mo++) {
576 		if ((u_int)off < mo->mo_uaddr)
577 			continue;
578 		u = off - mo->mo_uaddr;
579 		sz = mo->mo_size ? mo->mo_size : sc->sc_fb.fb_type.fb_size;
580 		if (u < sz)
581 			return ((int)sc->sc_physadr + u + mo->mo_physoff +
582 			    PMAP_OBIO + PMAP_NC);
583 	}
584 #ifdef DEBUG
585 	{
586 	  register struct proc *p = curproc;	/* XXX */
587 	  log(LOG_NOTICE, "cgsixmap(%x) (%s[%d])\n", off, p->p_comm, p->p_pid);
588 	}
589 #endif
590 	return (-1);	/* not a user-map offset */
591 }
592