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