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
cgsixattach(parent,self,args)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
cgsixopen(dev,flags,mode,p)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
cgsixclose(dev,flags,mode,p)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
cgsixioctl(dev,cmd,data,flags,p)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
cg6_reset(sc)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
cg6_setcursor(sc)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
cg6_loadcursor(sc)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
cg6_loadcmap(sc,start,ncolors)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
cg6_loadomap(sc)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
cg6_unblank(dev)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
cgsixmap(dev,off,prot)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