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