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