xref: /original-bsd/sys/pmax/dev/cfb.c (revision be1f24e8)
1 /*-
2  * Copyright (c) 1992 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Ralph Campbell.
7  *
8  * %sccs.include.redist.c%
9  *
10  *	@(#)cfb.c	7.4 (Berkeley) 10/11/92
11  */
12 
13 /*
14  *  devGraphics.c --
15  *
16  *     	This file contains machine-dependent routines for the graphics device.
17  *
18  *	Copyright (C) 1989 Digital Equipment Corporation.
19  *	Permission to use, copy, modify, and distribute this software and
20  *	its documentation for any purpose and without fee is hereby granted,
21  *	provided that the above copyright notice appears in all copies.
22  *	Digital Equipment Corporation makes no representations about the
23  *	suitability of this software for any purpose.  It is provided "as is"
24  *	without express or implied warranty.
25  *
26  * from: $Header: /sprite/src/kernel/dev/ds3100.md/RCS/devGraphics.c,
27  *	v 9.2 90/02/13 22:16:24 shirriff Exp $ SPRITE (DECWRL)";
28  */
29 /*
30  * Mach Operating System
31  * Copyright (c) 1991,1990,1989 Carnegie Mellon University
32  * All Rights Reserved.
33  *
34  * Permission to use, copy, modify and distribute this software and its
35  * documentation is hereby granted, provided that both the copyright
36  * notice and this permission notice appear in all copies of the
37  * software, derivative works or modified versions, and any portions
38  * thereof, and that both notices appear in supporting documentation.
39  *
40  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
41  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
42  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
43  *
44  * Carnegie Mellon requests users of this software to return to
45  *
46  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
47  *  School of Computer Science
48  *  Carnegie Mellon University
49  *  Pittsburgh PA 15213-3890
50  *
51  * any improvements or extensions that they make and grant Carnegie the
52  * rights to redistribute these changes.
53  */
54 
55 #include "cfb.h"
56 #if NCFB > 0
57 
58 /*
59  * This is a device driver for the PMAG-BA color frame buffer
60  * on the TURBOchannel.
61  * XXX This is just to get a console working;
62  *	it will need changes to work with X11R5.
63  */
64 
65 #include <sys/param.h>
66 #include <sys/time.h>
67 #include <sys/kernel.h>
68 #include <sys/ioctl.h>
69 #include <sys/file.h>
70 #include <sys/errno.h>
71 #include <sys/proc.h>
72 #include <sys/mman.h>
73 
74 #include <vm/vm.h>
75 
76 #include <machine/machConst.h>
77 #include <machine/machMon.h>
78 #include <machine/dc7085cons.h>
79 #include <machine/pmioctl.h>
80 
81 #include <pmax/dev/device.h>
82 #include <pmax/dev/cfbreg.h>
83 #include <pmax/dev/font.c>
84 
85 #define MAX_ROW	56
86 #define MAX_COL	80
87 
88 /*
89  * Macro to translate from a time struct to milliseconds.
90  */
91 #define TO_MS(tv) ((tv.tv_sec * 1000) + (tv.tv_usec / 1000))
92 
93 static int	isMono;		/* true if B&W frame buffer */
94 static int	initialized;	/* true if 'probe' was successful */
95 static int	GraphicsOpen;	/* true if the graphics device is open */
96 static int	row, col;	/* row and col for console cursor */
97 static struct	selinfo cfb_selp;	/* process waiting for select */
98 static unsigned	fb_addr;	/* frame buffer kernel virtual address */
99 static unsigned	planemask_addr;	/* plane mask kernel virtual address */
100 
101 /*
102  * These need to be mapped into user space.
103  */
104 static struct pmuaccess {
105 	PM_Info		scrInfo;
106 	pmEvent		events[PM_MAXEVQ];
107 	pmTimeCoord	tcs[MOTION_BUFFER_SIZE];
108 } pmu;
109 
110 /*
111  * Font mask bits used by Blitc().
112  */
113 static unsigned int fontmaskBits[16] = {
114 	0x00000000,
115 	0x00000001,
116 	0x00000100,
117 	0x00000101,
118 	0x00010000,
119 	0x00010001,
120 	0x00010100,
121 	0x00010101,
122 	0x01000000,
123 	0x01000001,
124 	0x01000100,
125 	0x01000101,
126 	0x01010000,
127 	0x01010001,
128 	0x01010100,
129 	0x01010101
130 };
131 
132 /*
133  * Forward references.
134  */
135 static void Scroll();
136 static void Blitc();
137 
138 static void ScreenInit();
139 static void LoadCursor();
140 static void RestoreCursorColor();
141 static void CursorColor();
142 static void PosCursor();
143 static void InitColorMap();
144 static void LoadColorMap();
145 static void EnableVideo();
146 static void DisableVideo();
147 
148 extern void dcKBDPutc();
149 extern void (*dcDivertXInput)();
150 extern void (*dcMouseEvent)();
151 extern void (*dcMouseButtons)();
152 
153 int	cfbprobe();
154 struct	driver cfbdriver = {
155 	"cfb", cfbprobe, 0, 0,
156 };
157 
158 /*
159  * Test to see if device is present.
160  * Return true if found and initialized ok.
161  */
162 cfbprobe(cp)
163 	register struct pmax_ctlr *cp;
164 {
165 
166 	if (!initialized) {
167 		if (!cfb_init(cp))
168 			return (0);
169 	}
170 	printf("cfb%d at nexus0 csr 0x%x priority %d\n",
171 		cp->pmax_unit, cp->pmax_addr, cp->pmax_pri);
172 	return (1);
173 }
174 
175 /*
176  *----------------------------------------------------------------------
177  *
178  * cfbKbdEvent --
179  *
180  *	Process a received character.
181  *
182  * Results:
183  *	None.
184  *
185  * Side effects:
186  *	Events added to the queue.
187  *
188  *----------------------------------------------------------------------
189  */
190 void
191 cfbKbdEvent(ch)
192 	int ch;
193 {
194 	register pmEvent *eventPtr;
195 	int i;
196 
197 	if (!GraphicsOpen)
198 		return;
199 
200 	/*
201 	 * See if there is room in the queue.
202 	 */
203 	i = PM_EVROUND(pmu.scrInfo.qe.eTail + 1);
204 	if (i == pmu.scrInfo.qe.eHead)
205 		return;
206 
207 	/*
208 	 * Add the event to the queue.
209 	 */
210 	eventPtr = &pmu.events[pmu.scrInfo.qe.eTail];
211 	eventPtr->type = BUTTON_RAW_TYPE;
212 	eventPtr->device = KEYBOARD_DEVICE;
213 	eventPtr->x = pmu.scrInfo.mouse.x;
214 	eventPtr->y = pmu.scrInfo.mouse.y;
215 	eventPtr->time = TO_MS(time);
216 	eventPtr->key = ch;
217 	pmu.scrInfo.qe.eTail = i;
218 	selwakeup(&cfb_selp);
219 }
220 
221 /*
222  *----------------------------------------------------------------------
223  *
224  * cfbMouseEvent --
225  *
226  *	Process a mouse event.
227  *
228  * Results:
229  *	None.
230  *
231  * Side effects:
232  *	An event is added to the event queue.
233  *
234  *----------------------------------------------------------------------
235  */
236 void
237 cfbMouseEvent(newRepPtr)
238 	register MouseReport *newRepPtr;
239 {
240 	unsigned milliSec;
241 	int i;
242 	pmEvent *eventPtr;
243 
244 	if (!GraphicsOpen)
245 		return;
246 
247 	milliSec = TO_MS(time);
248 
249 	/*
250 	 * Check to see if we have to accelerate the mouse
251 	 */
252 	if (pmu.scrInfo.mscale >= 0) {
253 		if (newRepPtr->dx >= pmu.scrInfo.mthreshold) {
254 			newRepPtr->dx +=
255 				(newRepPtr->dx - pmu.scrInfo.mthreshold) *
256 				pmu.scrInfo.mscale;
257 		}
258 		if (newRepPtr->dy >= pmu.scrInfo.mthreshold) {
259 			newRepPtr->dy +=
260 				(newRepPtr->dy - pmu.scrInfo.mthreshold) *
261 				pmu.scrInfo.mscale;
262 		}
263 	}
264 
265 	/*
266 	 * Update mouse position
267 	 */
268 	if (newRepPtr->state & MOUSE_X_SIGN) {
269 		pmu.scrInfo.mouse.x += newRepPtr->dx;
270 		if (pmu.scrInfo.mouse.x > pmu.scrInfo.max_cur_x)
271 			pmu.scrInfo.mouse.x = pmu.scrInfo.max_cur_x;
272 	} else {
273 		pmu.scrInfo.mouse.x -= newRepPtr->dx;
274 		if (pmu.scrInfo.mouse.x < pmu.scrInfo.min_cur_x)
275 			pmu.scrInfo.mouse.x = pmu.scrInfo.min_cur_x;
276 	}
277 	if (newRepPtr->state & MOUSE_Y_SIGN) {
278 		pmu.scrInfo.mouse.y -= newRepPtr->dy;
279 		if (pmu.scrInfo.mouse.y < pmu.scrInfo.min_cur_y)
280 			pmu.scrInfo.mouse.y = pmu.scrInfo.min_cur_y;
281 	} else {
282 		pmu.scrInfo.mouse.y += newRepPtr->dy;
283 		if (pmu.scrInfo.mouse.y > pmu.scrInfo.max_cur_y)
284 			pmu.scrInfo.mouse.y = pmu.scrInfo.max_cur_y;
285 	}
286 
287 	/*
288 	 * Move the hardware cursor.
289 	 */
290 	PosCursor(pmu.scrInfo.mouse.x, pmu.scrInfo.mouse.y);
291 
292 	/*
293 	 * Store the motion event in the motion buffer.
294 	 */
295 	pmu.tcs[pmu.scrInfo.qe.tcNext].time = milliSec;
296 	pmu.tcs[pmu.scrInfo.qe.tcNext].x = pmu.scrInfo.mouse.x;
297 	pmu.tcs[pmu.scrInfo.qe.tcNext].y = pmu.scrInfo.mouse.y;
298 	if (++pmu.scrInfo.qe.tcNext >= MOTION_BUFFER_SIZE)
299 		pmu.scrInfo.qe.tcNext = 0;
300 	if (pmu.scrInfo.mouse.y < pmu.scrInfo.mbox.bottom &&
301 	    pmu.scrInfo.mouse.y >=  pmu.scrInfo.mbox.top &&
302 	    pmu.scrInfo.mouse.x < pmu.scrInfo.mbox.right &&
303 	    pmu.scrInfo.mouse.x >=  pmu.scrInfo.mbox.left)
304 		return;
305 
306 	pmu.scrInfo.mbox.bottom = 0;
307 	if (PM_EVROUND(pmu.scrInfo.qe.eTail + 1) == pmu.scrInfo.qe.eHead)
308 		return;
309 
310 	i = PM_EVROUND(pmu.scrInfo.qe.eTail - 1);
311 	if ((pmu.scrInfo.qe.eTail != pmu.scrInfo.qe.eHead) &&
312 	    (i != pmu.scrInfo.qe.eHead)) {
313 		pmEvent *eventPtr;
314 
315 		eventPtr = &pmu.events[i];
316 		if (eventPtr->type == MOTION_TYPE) {
317 			eventPtr->x = pmu.scrInfo.mouse.x;
318 			eventPtr->y = pmu.scrInfo.mouse.y;
319 			eventPtr->time = milliSec;
320 			eventPtr->device = MOUSE_DEVICE;
321 			return;
322 		}
323 	}
324 	/*
325 	 * Put event into queue and wakeup any waiters.
326 	 */
327 	eventPtr = &pmu.events[pmu.scrInfo.qe.eTail];
328 	eventPtr->type = MOTION_TYPE;
329 	eventPtr->time = milliSec;
330 	eventPtr->x = pmu.scrInfo.mouse.x;
331 	eventPtr->y = pmu.scrInfo.mouse.y;
332 	eventPtr->device = MOUSE_DEVICE;
333 	pmu.scrInfo.qe.eTail = PM_EVROUND(pmu.scrInfo.qe.eTail + 1);
334 	selwakeup(&cfb_selp);
335 }
336 
337 /*
338  *----------------------------------------------------------------------
339  *
340  * cfbMouseButtons --
341  *
342  *	Process mouse buttons.
343  *
344  * Results:
345  *	None.
346  *
347  * Side effects:
348  *	None.
349  *
350  *----------------------------------------------------------------------
351  */
352 void
353 cfbMouseButtons(newRepPtr)
354 	MouseReport *newRepPtr;
355 {
356 	static char temp, oldSwitch, newSwitch;
357 	int i, j;
358 	pmEvent *eventPtr;
359 	static MouseReport lastRep;
360 
361 	if (!GraphicsOpen)
362 		return;
363 
364 	newSwitch = newRepPtr->state & 0x07;
365 	oldSwitch = lastRep.state & 0x07;
366 
367 	temp = oldSwitch ^ newSwitch;
368 	if (temp == 0)
369 		return;
370 	for (j = 1; j < 8; j <<= 1) {
371 		if ((j & temp) == 0)
372 			continue;
373 
374 		/*
375 		 * Check for room in the queue
376 		 */
377 		i = PM_EVROUND(pmu.scrInfo.qe.eTail+1);
378 		if (i == pmu.scrInfo.qe.eHead)
379 			return;
380 
381 		/*
382 		 * Put event into queue.
383 		 */
384 		eventPtr = &pmu.events[pmu.scrInfo.qe.eTail];
385 
386 		switch (j) {
387 		case RIGHT_BUTTON:
388 			eventPtr->key = EVENT_RIGHT_BUTTON;
389 			break;
390 
391 		case MIDDLE_BUTTON:
392 			eventPtr->key = EVENT_MIDDLE_BUTTON;
393 			break;
394 
395 		case LEFT_BUTTON:
396 			eventPtr->key = EVENT_LEFT_BUTTON;
397 		}
398 		if (newSwitch & j)
399 			eventPtr->type = BUTTON_DOWN_TYPE;
400 		else
401 			eventPtr->type = BUTTON_UP_TYPE;
402 		eventPtr->device = MOUSE_DEVICE;
403 
404 		eventPtr->time = TO_MS(time);
405 		eventPtr->x = pmu.scrInfo.mouse.x;
406 		eventPtr->y = pmu.scrInfo.mouse.y;
407 	}
408 	pmu.scrInfo.qe.eTail = i;
409 	selwakeup(&cfb_selp);
410 
411 	lastRep = *newRepPtr;
412 	pmu.scrInfo.mswitches = newSwitch;
413 }
414 
415 /*
416  *----------------------------------------------------------------------
417  *
418  * Scroll --
419  *
420  *	Scroll the screen.
421  *
422  * Results:
423  *	None.
424  *
425  * Side effects:
426  *	None.
427  *
428  *----------------------------------------------------------------------
429  */
430 static void
431 Scroll()
432 {
433 	register int *dest, *src;
434 	register int *end;
435 	register int temp0, temp1, temp2, temp3;
436 	register int i, scanInc, lineCount;
437 	int line;
438 
439 	/*
440 	 * If the mouse is on we don't scroll so that the bit map remains sane.
441 	 */
442 	if (GraphicsOpen) {
443 		row = 0;
444 		return;
445 	}
446 
447 	/*
448 	 *  The following is an optimization to cause the scrolling
449 	 *  of text to be memory limited.  Basically the writebuffer is
450 	 *  4 words (32 bits ea.) long so to achieve maximum speed we
451 	 *  read and write in multiples of 4 words. We also limit the
452 	 *  size to be MAX_COL characters for more speed.
453 	 */
454 	if (isMono) {
455 		lineCount = 5;
456 		line = 1920 * 2;
457 		scanInc = 44;
458 	} else {
459 		lineCount = 40;
460 		scanInc = 96;
461 		line = 1920 * 8;
462 	}
463 	src = (int *)(fb_addr + line);
464 	dest = (int *)(fb_addr);
465 	end = (int *)(fb_addr + (60 * line) - line);
466 	do {
467 		i = 0;
468 		do {
469 			temp0 = src[0];
470 			temp1 = src[1];
471 			temp2 = src[2];
472 			temp3 = src[3];
473 			dest[0] = temp0;
474 			dest[1] = temp1;
475 			dest[2] = temp2;
476 			dest[3] = temp3;
477 			dest += 4;
478 			src += 4;
479 			i++;
480 		} while (i < lineCount);
481 		src += scanInc;
482 		dest += scanInc;
483 	} while (src < end);
484 
485 	/*
486 	 * Now zero out the last two lines
487 	 */
488 	bzero(fb_addr + (row * line), 3 * line);
489 }
490 
491 /*
492  *----------------------------------------------------------------------
493  *
494  * cfbPutc --
495  *
496  *	Write a character to the console.
497  *
498  * Results:
499  *	None.
500  *
501  * Side effects:
502  *	None.
503  *
504  *----------------------------------------------------------------------
505  */
506 cfbPutc(c)
507 	register int c;
508 {
509 	int s;
510 
511 	s = splhigh();	/* in case we do any printf's at interrupt time */
512 	if (initialized) {
513 #ifdef DEBUG
514 		/*
515 		 * If the HELP key is pressed, wait for another
516 		 * HELP key press to start/stop output.
517 		 */
518 		if (dcDebugGetc() == LK_HELP) {
519 			while (dcDebugGetc() != LK_HELP)
520 				;
521 		}
522 #endif
523 		Blitc(c);
524 	} else {
525 		void (*f)() = (void (*)())MACH_MON_PUTCHAR;
526 
527 		(*f)(c);
528 	}
529 	splx(s);
530 }
531 
532 /*
533  *----------------------------------------------------------------------
534  *
535  * Blitc --
536  *
537  *	Write a character to the screen.
538  *
539  * Results:
540  *	None.
541  *
542  * Side effects:
543  *	None.
544  *
545  *----------------------------------------------------------------------
546  */
547 static void
548 Blitc(c)
549 	register int c;
550 {
551 	register char *bRow, *fRow;
552 	register int i;
553 	register int ote = isMono ? 256 : 1024; /* offset to table entry */
554 	int colMult = isMono ? 1 : 8;
555 
556 	c &= 0xff;
557 
558 	switch (c) {
559 	case '\t':
560 		for (i = 8 - (col & 0x7); i > 0; i--)
561 			Blitc(' ');
562 		break;
563 
564 	case '\r':
565 		col = 0;
566 		break;
567 
568 	case '\b':
569 		col--;
570 		if (col < 0)
571 			col = 0;
572 		break;
573 
574 	case '\n':
575 		if (row + 1 >= MAX_ROW)
576 			Scroll();
577 		else
578 			row++;
579 		col = 0;
580 		break;
581 
582 	case '\007':
583 		dcKBDPutc(LK_RING_BELL);
584 		break;
585 
586 	default:
587 		/*
588 		 * 0xA1 to 0xFD are the printable characters added with 8-bit
589 		 * support.
590 		 */
591 		if (c < ' ' || c > '~' && c < 0xA1 || c > 0xFD)
592 			break;
593 		/*
594 		 * If the next character will wrap around then
595 		 * increment row counter or scroll screen.
596 		 */
597 		if (col >= MAX_COL) {
598 			col = 0;
599 			if (row + 1 >= MAX_ROW)
600 				Scroll();
601 			else
602 				row++;
603 		}
604 		bRow = (char *)(fb_addr +
605 			(row * 15 & 0x3ff) * ote + col * colMult);
606 		i = c - ' ';
607 		/*
608 		 * This is to skip the (32) 8-bit
609 		 * control chars, as well as DEL
610 		 * and 0xA0 which aren't printable
611 		 */
612 		if (c > '~')
613 			i -= 34;
614 		i *= 15;
615 		fRow = (char *)((int)pmFont + i);
616 
617 		/* inline expansion for speed */
618 		if (isMono) {
619 			*bRow = *fRow++; bRow += ote;
620 			*bRow = *fRow++; bRow += ote;
621 			*bRow = *fRow++; bRow += ote;
622 			*bRow = *fRow++; bRow += ote;
623 			*bRow = *fRow++; bRow += ote;
624 			*bRow = *fRow++; bRow += ote;
625 			*bRow = *fRow++; bRow += ote;
626 			*bRow = *fRow++; bRow += ote;
627 			*bRow = *fRow++; bRow += ote;
628 			*bRow = *fRow++; bRow += ote;
629 			*bRow = *fRow++; bRow += ote;
630 			*bRow = *fRow++; bRow += ote;
631 			*bRow = *fRow++; bRow += ote;
632 			*bRow = *fRow++; bRow += ote;
633 			*bRow = *fRow++; bRow += ote;
634 		} else {
635 			register int j;
636 			register unsigned int *pInt;
637 
638 			pInt = (unsigned int *)bRow;
639 			for (j = 0; j < 15; j++) {
640 				/*
641 				 * fontmaskBits converts a nibble
642 				 * (4 bytes) to a long word
643 				 * containing 4 pixels corresponding
644 				 * to each bit in the nibble.  Thus
645 				 * we write two longwords for each
646 				 * byte in font.
647 				 *
648 				 * Remember the font is 8 bits wide
649 				 * and 15 bits high.
650 				 *
651 				 * We add 256 to the pointer to
652 				 * point to the pixel on the
653 				 * next scan line
654 				 * directly below the current
655 				 * pixel.
656 				 */
657 				pInt[0] = fontmaskBits[(*fRow) & 0xf];
658 				pInt[1] = fontmaskBits[((*fRow) >> 4) & 0xf];
659 				fRow++;
660 				pInt += 256;
661 			}
662 		}
663 		col++; /* increment column counter */
664 	}
665 	if (!GraphicsOpen)
666 		PosCursor(col * 8, row * 15);
667 }
668 
669 /*ARGSUSED*/
670 cfbopen(dev, flag)
671 	dev_t dev;
672 	int flag;
673 {
674 
675 	if (!initialized)
676 		return (ENXIO);
677 	if (GraphicsOpen)
678 		return (EBUSY);
679 
680 	GraphicsOpen = 1;
681 	if (!isMono)
682 		InitColorMap();
683 	/*
684 	 * Set up event queue for later
685 	 */
686 	pmu.scrInfo.qe.eSize = PM_MAXEVQ;
687 	pmu.scrInfo.qe.eHead = pmu.scrInfo.qe.eTail = 0;
688 	pmu.scrInfo.qe.tcSize = MOTION_BUFFER_SIZE;
689 	pmu.scrInfo.qe.tcNext = 0;
690 	pmu.scrInfo.qe.timestamp_ms = TO_MS(time);
691 	return (0);
692 }
693 
694 /*ARGSUSED*/
695 cfbclose(dev, flag)
696 	dev_t dev;
697 	int flag;
698 {
699 	int s;
700 
701 	if (!GraphicsOpen)
702 		return (EBADF);
703 
704 	GraphicsOpen = 0;
705 	if (!isMono)
706 		InitColorMap();
707 	s = spltty();
708 	dcDivertXInput = (void (*)())0;
709 	dcMouseEvent = (void (*)())0;
710 	dcMouseButtons = (void (*)())0;
711 	splx(s);
712 	ScreenInit();
713 	vmUserUnmap();
714 	bzero(fb_addr, (isMono ? 1024 / 8 : 1024) * 864);
715 	PosCursor(col * 8, row * 15);
716 	return (0);
717 }
718 
719 /*ARGSUSED*/
720 cfbioctl(dev, cmd, data, flag)
721 	dev_t dev;
722 	caddr_t data;
723 {
724 	int s;
725 
726 	switch (cmd) {
727 	case QIOCGINFO:
728 	    {
729 		caddr_t addr;
730 		extern caddr_t vmUserMap();
731 
732 		/*
733 		 * Map the all the data the user needs access to into
734 		 * user space.
735 		 */
736 		addr = vmUserMap(sizeof(pmu), (unsigned)&pmu);
737 		if (addr == (caddr_t)0)
738 			goto mapError;
739 		*(PM_Info **)data = &((struct pmuaccess *)addr)->scrInfo;
740 		pmu.scrInfo.qe.events = ((struct pmuaccess *)addr)->events;
741 		pmu.scrInfo.qe.tcs = ((struct pmuaccess *)addr)->tcs;
742 		/*
743 		 * Map the plane mask into the user's address space.
744 		 */
745 		addr = vmUserMap(4, planemask_addr);
746 		if (addr == (caddr_t)0)
747 			goto mapError;
748 		pmu.scrInfo.planemask = (char *)addr;
749 		/*
750 		 * Map the frame buffer into the user's address space.
751 		 */
752 		addr = vmUserMap(isMono ? 256*1024 : 1024*1024, fb_addr);
753 		if (addr == (caddr_t)0)
754 			goto mapError;
755 		pmu.scrInfo.bitmap = (char *)addr;
756 		break;
757 
758 	mapError:
759 		vmUserUnmap();
760 		printf("Cannot map shared data structures\n");
761 		return (EIO);
762 	    }
763 
764 	case QIOCPMSTATE:
765 		/*
766 		 * Set mouse state.
767 		 */
768 		pmu.scrInfo.mouse = *(pmCursor *)data;
769 		PosCursor(pmu.scrInfo.mouse.x, pmu.scrInfo.mouse.y);
770 		break;
771 
772 	case QIOCINIT:
773 		/*
774 		 * Initialize the screen.
775 		 */
776 		ScreenInit();
777 		break;
778 
779 	case QIOCKPCMD:
780 	    {
781 		pmKpCmd *kpCmdPtr;
782 		unsigned char *cp;
783 
784 		kpCmdPtr = (pmKpCmd *)data;
785 		if (kpCmdPtr->nbytes == 0)
786 			kpCmdPtr->cmd |= 0x80;
787 		if (!GraphicsOpen)
788 			kpCmdPtr->cmd |= 1;
789 		dcKBDPutc((int)kpCmdPtr->cmd);
790 		cp = &kpCmdPtr->par[0];
791 		for (; kpCmdPtr->nbytes > 0; cp++, kpCmdPtr->nbytes--) {
792 			if (kpCmdPtr->nbytes == 1)
793 				*cp |= 0x80;
794 			dcKBDPutc((int)*cp);
795 		}
796 		break;
797 	    }
798 
799 	case QIOCADDR:
800 		*(PM_Info **)data = &pmu.scrInfo;
801 		break;
802 
803 	case QIOWCURSOR:
804 		LoadCursor((unsigned short *)data);
805 		break;
806 
807 	case QIOWCURSORCOLOR:
808 		CursorColor((unsigned int *)data);
809 		break;
810 
811 	case QIOSETCMAP:
812 		LoadColorMap((ColorMap *)data);
813 		break;
814 
815 	case QIOKERNLOOP:
816 		s = spltty();
817 		dcDivertXInput = cfbKbdEvent;
818 		dcMouseEvent = cfbMouseEvent;
819 		dcMouseButtons = cfbMouseButtons;
820 		splx(s);
821 		break;
822 
823 	case QIOKERNUNLOOP:
824 		s = spltty();
825 		dcDivertXInput = (void (*)())0;
826 		dcMouseEvent = (void (*)())0;
827 		dcMouseButtons = (void (*)())0;
828 		splx(s);
829 		break;
830 
831 	case QIOVIDEOON:
832 		EnableVideo();
833 		break;
834 
835 	case QIOVIDEOOFF:
836 		DisableVideo();
837 		break;
838 
839 	default:
840 		printf("cfb0: Unknown ioctl command %x\n", cmd);
841 		return (EINVAL);
842 	}
843 	return (0);
844 }
845 
846 cfbselect(dev, flag, p)
847 	dev_t dev;
848 	int flag;
849 	struct proc *p;
850 {
851 
852 	switch (flag) {
853 	case FREAD:
854 		if (pmu.scrInfo.qe.eHead != pmu.scrInfo.qe.eTail)
855 			return (1);
856 		selrecord(p, &cfb_selp);
857 		break;
858 	}
859 
860 	return (0);
861 }
862 
863 static u_char	cursor_RGB[6];	/* cursor color 2 & 3 */
864 
865 /*
866  * The default cursor.
867  */
868 static unsigned short defCursor[1024] = {
869 	0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
870 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
871 	0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
872 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
873 	0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
874 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
875 	0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
876 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
877 	0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
878 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
879 	0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
880 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
881 	0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
882 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
883 	0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
884 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
885 	0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
886 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
887 	0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
888 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
889 	0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
890 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
891 	0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
892 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
893 	0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
894 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
895 	0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
896 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
897 	0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
898 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
899 	0x00FF, 0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
900 	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
901 };
902 
903 #define	CFB_OFFSET_VRAM		0x0		/* from module's base */
904 						/* Replicated at x100000 */
905 #define CFB_OFFSET_BT459	0x200000	/* Bt459 registers */
906 #define CFB_OFFSET_IREQ		0x300000	/* Interrupt req. control */
907 #define CFB_OFFSET_ROM		0x380000	/* Diagnostic ROM */
908 #define CFB_OFFSET_RESET	0x3c0000	/* Bt459 resets on writes */
909 
910 /*
911  * Generic register access
912  */
913 void
914 bt459_select_reg(regs, regno)
915 	bt459_regmap_t *regs;
916 {
917 	regs->addr_lo = regno;
918 	regs->addr_hi = regno >> 8;
919 	MachEmptyWriteBuffer();
920 }
921 
922 void
923 bt459_write_reg(regs, regno, val)
924 	bt459_regmap_t *regs;
925 {
926 	regs->addr_lo = regno;
927 	regs->addr_hi = regno >> 8;
928 	MachEmptyWriteBuffer();
929 	regs->addr_reg = val;
930 	MachEmptyWriteBuffer();
931 }
932 
933 unsigned char
934 bt459_read_reg(regs, regno)
935 	bt459_regmap_t *regs;
936 {
937 	regs->addr_lo = regno;
938 	regs->addr_hi = regno >> 8;
939 	MachEmptyWriteBuffer();
940 	return regs->addr_reg;
941 }
942 
943 #ifdef DEBUG
944 bt459_print_colormap(regs)
945 	bt459_regmap_t *regs;
946 {
947 	register int i;
948 
949 	bt459_select_reg(regs, 0);
950 	for (i = 0; i < 256; i++) {
951 		register unsigned red, green, blue;
952 
953 		red = regs->addr_cmap;
954 		green = regs->addr_cmap;
955 		blue = regs->addr_cmap;
956 		printf("%x->[x%x x%x x%x]\n", i, red, green, blue);
957 	}
958 }
959 #endif
960 
961 /*
962  * Test to see if device is present.
963  * Return true if found and initialized ok.
964  */
965 cfb_init(cp)
966 	register struct pmax_ctlr *cp;
967 {
968 	bt459_regmap_t *regs;
969 
970 	/* check for no frame buffer */
971 	if (badaddr(cp->pmax_addr, 4))
972 		return (0);
973 
974 	fb_addr = (unsigned)cp->pmax_addr + CFB_OFFSET_VRAM;
975 	planemask_addr = 0; /* XXX */
976 	regs = (bt459_regmap_t *)(fb_addr + CFB_OFFSET_BT459);
977 
978 	if (bt459_read_reg(regs, BT459_REG_ID) != 0x4a)
979 		return (0);
980 
981 	*(int *)(fb_addr + CFB_OFFSET_RESET) = 0;	/* force chip reset */
982 	DELAY(2000);	/* ???? check right time on specs! ???? */
983 
984 	/* use 4:1 input mux */
985 	bt459_write_reg(regs, BT459_REG_CMD0, 0x40);
986 
987 	/* no zooming, no panning */
988 	bt459_write_reg(regs, BT459_REG_CMD1, 0x00);
989 
990 	/*
991 	 * signature test, X-windows cursor, no overlays, SYNC* PLL,
992 	 * normal RAM select, 7.5 IRE pedestal, do sync
993 	 */
994 	bt459_write_reg(regs, BT459_REG_CMD2, 0xc2);
995 
996 	/* get all pixel bits */
997 	bt459_write_reg(regs, BT459_REG_PRM, 0xff);
998 
999 	/* no blinking */
1000 	bt459_write_reg(regs, BT459_REG_PBM, 0x00);
1001 
1002 	/* no overlay */
1003 	bt459_write_reg(regs, BT459_REG_ORM, 0x00);
1004 
1005 	/* no overlay blink */
1006 	bt459_write_reg(regs, BT459_REG_OBM, 0x00);
1007 
1008 	/* no interleave, no underlay */
1009 	bt459_write_reg(regs, BT459_REG_ILV, 0x00);
1010 
1011 	/* normal operation, no signature analysis */
1012 	bt459_write_reg(regs, BT459_REG_TEST, 0x00);
1013 
1014 	/*
1015 	 * no blinking, 1bit cross hair, XOR reg&crosshair,
1016 	 * no crosshair on either plane 0 or 1,
1017 	 * regular cursor on both planes.
1018 	 */
1019 	bt459_write_reg(regs, BT459_REG_CCR, 0xc0);
1020 
1021 	/* home cursor */
1022 	bt459_write_reg(regs, BT459_REG_CXLO, 0x00);
1023 	bt459_write_reg(regs, BT459_REG_CXHI, 0x00);
1024 	bt459_write_reg(regs, BT459_REG_CYLO, 0x00);
1025 	bt459_write_reg(regs, BT459_REG_CYHI, 0x00);
1026 
1027 	/* no crosshair window */
1028 	bt459_write_reg(regs, BT459_REG_WXLO, 0x00);
1029 	bt459_write_reg(regs, BT459_REG_WXHI, 0x00);
1030 	bt459_write_reg(regs, BT459_REG_WYLO, 0x00);
1031 	bt459_write_reg(regs, BT459_REG_WYHI, 0x00);
1032 	bt459_write_reg(regs, BT459_REG_WWLO, 0x00);
1033 	bt459_write_reg(regs, BT459_REG_WWHI, 0x00);
1034 	bt459_write_reg(regs, BT459_REG_WHLO, 0x00);
1035 	bt459_write_reg(regs, BT459_REG_WHHI, 0x00);
1036 
1037 	/*
1038 	 * Initialize screen info.
1039 	 */
1040 	pmu.scrInfo.max_row = MAX_ROW;
1041 	pmu.scrInfo.max_col = MAX_COL;
1042 	pmu.scrInfo.max_x = 1024;
1043 	pmu.scrInfo.max_y = 864;
1044 	pmu.scrInfo.max_cur_x = 1023;
1045 	pmu.scrInfo.max_cur_y = 863;
1046 	pmu.scrInfo.version = 11;
1047 	pmu.scrInfo.mthreshold = 4;
1048 	pmu.scrInfo.mscale = 2;
1049 	pmu.scrInfo.min_cur_x = 0;
1050 	pmu.scrInfo.min_cur_y = 0;
1051 	pmu.scrInfo.qe.timestamp_ms = TO_MS(time);
1052 	pmu.scrInfo.qe.eSize = PM_MAXEVQ;
1053 	pmu.scrInfo.qe.eHead = pmu.scrInfo.qe.eTail = 0;
1054 	pmu.scrInfo.qe.tcSize = MOTION_BUFFER_SIZE;
1055 	pmu.scrInfo.qe.tcNext = 0;
1056 
1057 	/*
1058 	 * Initialize the color map, the screen, and the mouse.
1059 	 */
1060 	InitColorMap();
1061 	ScreenInit();
1062 	Scroll();
1063 
1064 	initialized = 1;
1065 	return (1);
1066 }
1067 
1068 /*
1069  * ----------------------------------------------------------------------------
1070  *
1071  * ScreenInit --
1072  *
1073  *	Initialize the screen.
1074  *
1075  * Results:
1076  *	None.
1077  *
1078  * Side effects:
1079  *	The screen is initialized.
1080  *
1081  * ----------------------------------------------------------------------------
1082  */
1083 static void
1084 ScreenInit()
1085 {
1086 
1087 	/*
1088 	 * Home the cursor.
1089 	 * We want an LSI terminal emulation. We want the graphics
1090 	 * terminal to scroll from the bottom. So start at the bottom.
1091 	 */
1092 	row = 55;
1093 	col = 0;
1094 
1095 	/*
1096 	 * Load the cursor with the default values
1097 	 *
1098 	 */
1099 	LoadCursor(defCursor);
1100 }
1101 
1102 /*
1103  * ----------------------------------------------------------------------------
1104  *
1105  * LoadCursor --
1106  *
1107  *	Routine to load the cursor Sprite pattern.
1108  *
1109  * Results:
1110  *	None.
1111  *
1112  * Side effects:
1113  *	The cursor is loaded into the hardware cursor.
1114  *
1115  * ----------------------------------------------------------------------------
1116  */
1117 static void
1118 LoadCursor(cur)
1119 	unsigned short *cur;
1120 {
1121 	bt459_regmap_t *regs = (bt459_regmap_t *)(fb_addr + CFB_OFFSET_BT459);
1122 	register int i, j;
1123 
1124 	/*
1125 	 * As per specs, must run a check to see if we
1126 	 * had contention. If so, re-write the cursor.
1127 	 */
1128 	for (j = 0; j < 2; j++) {
1129 		/* loop once to write */
1130 		bt459_select_reg(regs, BT459_REG_CRAM_BASE);
1131 		for (i = 0; i < 1024; i++) {
1132 			regs->addr_reg = cur[i];
1133 			MachEmptyWriteBuffer();
1134 		}
1135 
1136 		/* loop to check, if fail write again */
1137 		bt459_select_reg(regs, BT459_REG_CRAM_BASE);
1138 		for (i = 0; i < 1024; i++)
1139 			if (regs->addr_reg != cur[i])
1140 				break;
1141 		if (i == 1024)
1142 			break;	/* all went well first shot */
1143 	}
1144 }
1145 
1146 /*
1147  * ----------------------------------------------------------------------------
1148  *
1149  * RestoreCursorColor --
1150  *
1151  *	Routine to restore the color of the cursor.
1152  *
1153  * Results:
1154  *	None.
1155  *
1156  * Side effects:
1157  *	None.
1158  *
1159  * ----------------------------------------------------------------------------
1160  */
1161 static void
1162 RestoreCursorColor()
1163 {
1164 	bt459_regmap_t *regs = (bt459_regmap_t *)(fb_addr + CFB_OFFSET_BT459);
1165 	register int i;
1166 
1167 	bt459_select_reg(regs, BT459_REG_CCOLOR_2);
1168 	for (i = 0; i < 6; i++) {
1169 		regs->addr_reg = cursor_RGB[i];
1170 		MachEmptyWriteBuffer();
1171 	}
1172 }
1173 
1174 /*
1175  * ----------------------------------------------------------------------------
1176  *
1177  * CursorColor --
1178  *
1179  *	Set the color of the cursor.
1180  *
1181  * Results:
1182  *	None.
1183  *
1184  * Side effects:
1185  *	None.
1186  *
1187  * ----------------------------------------------------------------------------
1188  */
1189 static void
1190 CursorColor(color)
1191 	unsigned int color[];
1192 {
1193 	register int i, j;
1194 
1195 	for (i = 0; i < 6; i++)
1196 		cursor_RGB[i] = (u_char)(color[i] >> 8);
1197 
1198 	RestoreCursorColor();
1199 }
1200 
1201 /*
1202  *----------------------------------------------------------------------
1203  *
1204  * PosCursor --
1205  *
1206  *	Postion the cursor.
1207  *
1208  * Results:
1209  *	None.
1210  *
1211  * Side effects:
1212  *	None.
1213  *
1214  *----------------------------------------------------------------------
1215  */
1216 static void
1217 PosCursor(x, y)
1218 	register int x, y;
1219 {
1220 	bt459_regmap_t *regs = (bt459_regmap_t *)(fb_addr + CFB_OFFSET_BT459);
1221 
1222 	if (y < pmu.scrInfo.min_cur_y || y > pmu.scrInfo.max_cur_y)
1223 		y = pmu.scrInfo.max_cur_y;
1224 	if (x < pmu.scrInfo.min_cur_x || x > pmu.scrInfo.max_cur_x)
1225 		x = pmu.scrInfo.max_cur_x;
1226 	pmu.scrInfo.cursor.x = x;		/* keep track of real cursor */
1227 	pmu.scrInfo.cursor.y = y;		/* position, indep. of mouse */
1228 
1229 	x += 219;
1230 	y += 34;
1231 
1232 	bt459_select_reg(regs, BT459_REG_CXLO);
1233 	regs->addr_reg = x;
1234 	MachEmptyWriteBuffer();
1235 	regs->addr_reg = x >> 8;
1236 	MachEmptyWriteBuffer();
1237 	regs->addr_reg = y;
1238 	MachEmptyWriteBuffer();
1239 	regs->addr_reg = y >> 8;
1240 	MachEmptyWriteBuffer();
1241 }
1242 
1243 /*
1244  * ----------------------------------------------------------------------------
1245  *
1246  * InitColorMap --
1247  *
1248  *	Initialize the color map.
1249  *
1250  * Results:
1251  *	None.
1252  *
1253  * Side effects:
1254  *	The colormap is initialized appropriately.
1255  *
1256  * ----------------------------------------------------------------------------
1257  */
1258 static void
1259 InitColorMap()
1260 {
1261 	bt459_regmap_t *regs = (bt459_regmap_t *)(fb_addr + CFB_OFFSET_BT459);
1262 	register int i;
1263 
1264 	bt459_select_reg(regs, 0);
1265 	regs->addr_cmap = 0; MachEmptyWriteBuffer();
1266 	regs->addr_cmap = 0; MachEmptyWriteBuffer();
1267 	regs->addr_cmap = 0; MachEmptyWriteBuffer();
1268 
1269 	for (i = 1; i < 256; i++) {
1270 		regs->addr_cmap = 0xff; MachEmptyWriteBuffer();
1271 		regs->addr_cmap = 0xff; MachEmptyWriteBuffer();
1272 		regs->addr_cmap = 0xff; MachEmptyWriteBuffer();
1273 	}
1274 
1275 	for (i = 0; i < 3; i++) {
1276 		cursor_RGB[i] = 0x00;
1277 		cursor_RGB[i + 3] = 0xff;
1278 	}
1279 	RestoreCursorColor();
1280 }
1281 
1282 /*
1283  * ----------------------------------------------------------------------------
1284  *
1285  * LoadColorMap --
1286  *
1287  *	Load the color map.
1288  *
1289  * Results:
1290  *	None.
1291  *
1292  * Side effects:
1293  *	The color map is loaded.
1294  *
1295  * ----------------------------------------------------------------------------
1296  */
1297 static void
1298 LoadColorMap(ptr)
1299 	ColorMap *ptr;
1300 {
1301 	bt459_regmap_t *regs = (bt459_regmap_t *)(fb_addr + CFB_OFFSET_BT459);
1302 
1303 	if (ptr->index > 256)
1304 		return;
1305 
1306 	bt459_select_reg(regs, ptr->index);
1307 
1308 	regs->addr_cmap = ptr->Entry.red; MachEmptyWriteBuffer();
1309 	regs->addr_cmap = ptr->Entry.green; MachEmptyWriteBuffer();
1310 	regs->addr_cmap = ptr->Entry.blue; MachEmptyWriteBuffer();
1311 }
1312 
1313 /*
1314  * Video on/off state.
1315  */
1316 struct vstate {
1317 	u_char	color0[3];	/* saved color map entry zero */
1318 	u_char	off;		/* TRUE if display is off */
1319 } vstate;
1320 
1321 /*
1322  * ----------------------------------------------------------------------------
1323  *
1324  * EnableVideo --
1325  *
1326  *	Enable the video display.
1327  *
1328  * Results:
1329  *	None.
1330  *
1331  * Side effects:
1332  *	The display is enabled.
1333  *
1334  * ----------------------------------------------------------------------------
1335  */
1336 static void
1337 EnableVideo()
1338 {
1339 	bt459_regmap_t *regs = (bt459_regmap_t *)(fb_addr + CFB_OFFSET_BT459);
1340 
1341 	if (!vstate.off)
1342 		return;
1343 
1344 	/* restore old color map entry zero */
1345 	bt459_select_reg(regs, 0);
1346 	regs->addr_cmap = vstate.color0[0];
1347 	MachEmptyWriteBuffer();
1348 	regs->addr_cmap = vstate.color0[1];
1349 	MachEmptyWriteBuffer();
1350 	regs->addr_cmap = vstate.color0[2];
1351 	MachEmptyWriteBuffer();
1352 
1353 	/* enable normal display */
1354 	bt459_write_reg(regs, BT459_REG_PRM, 0xff);
1355 	bt459_write_reg(regs, BT459_REG_CCR, 0xc0);
1356 
1357 	vstate.off = 0;
1358 }
1359 
1360 /*
1361  * ----------------------------------------------------------------------------
1362  *
1363  * DisableVideo --
1364  *
1365  *	Disable the video display.
1366  *
1367  * Results:
1368  *	None.
1369  *
1370  * Side effects:
1371  *	The display is disabled.
1372  *
1373  * ----------------------------------------------------------------------------
1374  */
1375 static void
1376 DisableVideo()
1377 {
1378 	bt459_regmap_t *regs = (bt459_regmap_t *)(fb_addr + CFB_OFFSET_BT459);
1379 
1380 	if (vstate.off)
1381 		return;
1382 
1383 	/* save old color map entry zero */
1384 	bt459_select_reg(regs, 0);
1385 	vstate.color0[0] = regs->addr_cmap;
1386 	vstate.color0[1] = regs->addr_cmap;
1387 	vstate.color0[2] = regs->addr_cmap;
1388 
1389 	/* set color map entry zero to zero */
1390 	bt459_select_reg(regs, 0);
1391 	regs->addr_cmap = 0;
1392 	MachEmptyWriteBuffer();
1393 	regs->addr_cmap = 0;
1394 	MachEmptyWriteBuffer();
1395 	regs->addr_cmap = 0;
1396 	MachEmptyWriteBuffer();
1397 
1398 	/* disable display */
1399 	bt459_write_reg(regs, BT459_REG_PRM, 0);
1400 	bt459_write_reg(regs, BT459_REG_CCR, 0);
1401 
1402 	vstate.off = 1;
1403 }
1404 #endif
1405