1 /*
2  *  linux/drivers/video/cyber2000fb.c
3  *
4  *  Copyright (C) 1998-2000 Russell King
5  *
6  *  MIPS and 50xx clock support
7  *  Copyright (C) 2001 Bradley D. LaRonde <brad@ltc.com>
8  *
9  *  32 bit support, text color and panning fixes for modes != 8 bit
10  *  Copyright (C) 2002 Denis Oliver Kropp <dok@directfb.org>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License version 2 as
14  * published by the Free Software Foundation.
15  *
16  * Intergraphics CyberPro 2000, 2010 and 5000 frame buffer device
17  *
18  * Based on cyberfb.c.
19  *
20  * Note that we now use the new fbcon fix, var and cmap scheme.  We do still
21  * have to check which console is the currently displayed one however, since
22  * especially for the colourmap stuff.  Once fbcon has been fully migrated,
23  * we can kill the last 5 references to cfb->currcon.
24  *
25  * We also use the new hotplug PCI subsystem.  I'm not sure if there are any
26  * such cards, but I'm erring on the side of caution.  We don't want to go
27  * pop just because someone does have one.
28  *
29  * Note that this doesn't work fully in the case of multiple CyberPro cards
30  * with grabbers.  We currently can only attach to the first CyberPro card
31  * found.
32  */
33 #include <linux/config.h>
34 #include <linux/module.h>
35 #include <linux/kernel.h>
36 #include <linux/errno.h>
37 #include <linux/string.h>
38 #include <linux/mm.h>
39 #include <linux/tty.h>
40 #include <linux/slab.h>
41 #include <linux/delay.h>
42 #include <linux/fb.h>
43 #include <linux/pci.h>
44 #include <linux/init.h>
45 
46 #include <asm/io.h>
47 #include <asm/irq.h>
48 #include <asm/pgtable.h>
49 #include <asm/system.h>
50 #include <asm/uaccess.h>
51 
52 #include <video/fbcon.h>
53 #include <video/fbcon-cfb8.h>
54 #include <video/fbcon-cfb16.h>
55 #include <video/fbcon-cfb24.h>
56 #include <video/fbcon-cfb32.h>
57 
58 /*
59  * Define this if you don't want RGB565, but RGB555 for 16bpp displays.
60  */
61 /*#define CFB16_IS_CFB15*/
62 
63 #include "cyber2000fb.h"
64 
65 struct cfb_info {
66 	struct fb_info		fb;
67 	struct display_switch	*dispsw;
68 	struct pci_dev		*dev;
69 	unsigned char 		*region;
70 	unsigned char		*regs;
71 	signed int		currcon;
72 	int			func_use_count;
73 	u_long			ref_ps;
74 
75 	/*
76 	 * Clock divisors
77 	 */
78 	u_int			divisors[4];
79 
80 	struct {
81 		u8 red, green, blue;
82 	} palette[NR_PALETTE];
83 
84 	u_char			mem_ctl1;
85 	u_char			mem_ctl2;
86 	u_char			mclk_mult;
87 	u_char			mclk_div;
88 };
89 
90 static char default_font_storage[40];
91 static char *default_font = "Acorn8x8";
92 MODULE_PARM(default_font, "s");
93 MODULE_PARM_DESC(default_font, "Default font name");
94 
95 /*
96  * Our access methods.
97  */
98 #define cyber2000fb_writel(val,reg,cfb)	writel(val, (cfb)->regs + (reg))
99 #define cyber2000fb_writew(val,reg,cfb)	writew(val, (cfb)->regs + (reg))
100 #define cyber2000fb_writeb(val,reg,cfb)	writeb(val, (cfb)->regs + (reg))
101 
102 #define cyber2000fb_readb(reg,cfb)	readb((cfb)->regs + (reg))
103 
104 static inline void
cyber2000_crtcw(unsigned int reg,unsigned int val,struct cfb_info * cfb)105 cyber2000_crtcw(unsigned int reg, unsigned int val, struct cfb_info *cfb)
106 {
107 	cyber2000fb_writew((reg & 255) | val << 8, 0x3d4, cfb);
108 }
109 
110 static inline void
cyber2000_grphw(unsigned int reg,unsigned int val,struct cfb_info * cfb)111 cyber2000_grphw(unsigned int reg, unsigned int val, struct cfb_info *cfb)
112 {
113 	cyber2000fb_writew((reg & 255) | val << 8, 0x3ce, cfb);
114 }
115 
116 static inline unsigned int
cyber2000_grphr(unsigned int reg,struct cfb_info * cfb)117 cyber2000_grphr(unsigned int reg, struct cfb_info *cfb)
118 {
119 	cyber2000fb_writeb(reg, 0x3ce, cfb);
120 	return cyber2000fb_readb(0x3cf, cfb);
121 }
122 
123 static inline void
cyber2000_attrw(unsigned int reg,unsigned int val,struct cfb_info * cfb)124 cyber2000_attrw(unsigned int reg, unsigned int val, struct cfb_info *cfb)
125 {
126 	cyber2000fb_readb(0x3da, cfb);
127 	cyber2000fb_writeb(reg, 0x3c0, cfb);
128 	cyber2000fb_readb(0x3c1, cfb);
129 	cyber2000fb_writeb(val, 0x3c0, cfb);
130 }
131 
132 static inline void
cyber2000_seqw(unsigned int reg,unsigned int val,struct cfb_info * cfb)133 cyber2000_seqw(unsigned int reg, unsigned int val, struct cfb_info *cfb)
134 {
135 	cyber2000fb_writew((reg & 255) | val << 8, 0x3c4, cfb);
136 }
137 
138 /* -------------------- Hardware specific routines ------------------------- */
139 
140 /*
141  * Hardware Cyber2000 Acceleration
142  */
cyber2000_accel_wait(struct cfb_info * cfb)143 static void cyber2000_accel_wait(struct cfb_info *cfb)
144 {
145 	int count = 100000;
146 
147 	while (cyber2000fb_readb(CO_REG_CONTROL, cfb) & 0x80) {
148 		if (!count--) {
149 			debug_printf("accel_wait timed out\n");
150 			cyber2000fb_writeb(0, CO_REG_CONTROL, cfb);
151 			return;
152 		}
153 		udelay(1);
154 	}
155 }
156 
cyber2000_accel_setup(struct display * p)157 static void cyber2000_accel_setup(struct display *p)
158 {
159 	struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
160 
161 	cfb->dispsw->setup(p);
162 }
163 
164 static void
cyber2000_accel_bmove(struct display * p,int sy,int sx,int dy,int dx,int height,int width)165 cyber2000_accel_bmove(struct display *p, int sy, int sx, int dy, int dx,
166 		      int height, int width)
167 {
168 	struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
169 	struct fb_var_screeninfo *var = &p->fb_info->var;
170 	u_long src, dst;
171 	u_int fh, fw;
172 	int cmd = CO_CMD_L_PATTERN_FGCOL;
173 
174 	fw    = fontwidth(p);
175 	sx    *= fw;
176 	dx    *= fw;
177 	width *= fw;
178 	width -= 1;
179 
180 	if (sx < dx) {
181 		sx += width;
182 		dx += width;
183 		cmd |= CO_CMD_L_INC_LEFT;
184 	}
185 
186 	fh     = fontheight(p);
187 	sy     *= fh;
188 	dy     *= fh;
189 	height *= fh;
190 	height -= 1;
191 
192 	if (sy < dy) {
193 		sy += height;
194 		dy += height;
195 		cmd |= CO_CMD_L_INC_UP;
196 	}
197 
198 	src    = sx + sy * var->xres_virtual;
199 	dst    = dx + dy * var->xres_virtual;
200 
201 	cyber2000_accel_wait(cfb);
202 	cyber2000fb_writeb(0x00,  CO_REG_CONTROL, cfb);
203 	cyber2000fb_writeb(0x03,  CO_REG_FORE_MIX, cfb);
204 	cyber2000fb_writew(width, CO_REG_WIDTH, cfb);
205 
206 	if (var->bits_per_pixel != 24) {
207 		cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);
208 		cyber2000fb_writel(src, CO_REG_SRC_PTR, cfb);
209 	} else {
210 		cyber2000fb_writel(dst * 3, CO_REG_DEST_PTR, cfb);
211 		cyber2000fb_writeb(dst,     CO_REG_X_PHASE, cfb);
212 		cyber2000fb_writel(src * 3, CO_REG_SRC_PTR, cfb);
213 	}
214 
215 	cyber2000fb_writew(height, CO_REG_HEIGHT, cfb);
216 	cyber2000fb_writew(cmd,    CO_REG_CMD_L, cfb);
217 	cyber2000fb_writew(0x2800, CO_REG_CMD_H, cfb);
218 }
219 
220 static void
cyber2000_accel_clear(struct vc_data * conp,struct display * p,int sy,int sx,int height,int width)221 cyber2000_accel_clear(struct vc_data *conp, struct display *p, int sy, int sx,
222 		      int height, int width)
223 {
224 	struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
225 	struct fb_var_screeninfo *var = &p->fb_info->var;
226 	u_long dst;
227 	u_int fw, fh;
228 	u32 bgx = attr_bgcol_ec(p, conp);
229 
230 	fw = fontwidth(p);
231 	fh = fontheight(p);
232 
233 	dst    = sx * fw + sy * var->xres_virtual * fh;
234 	width  = width * fw - 1;
235 	height = height * fh - 1;
236 
237 	cyber2000_accel_wait(cfb);
238 	cyber2000fb_writeb(0x00,   CO_REG_CONTROL,  cfb);
239 	cyber2000fb_writeb(0x03,   CO_REG_FORE_MIX, cfb);
240 	cyber2000fb_writew(width,  CO_REG_WIDTH,    cfb);
241 	cyber2000fb_writew(height, CO_REG_HEIGHT,   cfb);
242 
243 	switch (var->bits_per_pixel) {
244 	case 15:
245 	case 16:
246 		bgx = ((u16 *)p->dispsw_data)[bgx];
247 	case 8:
248 		cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);
249 		break;
250 
251 	case 24:
252 		cyber2000fb_writel(dst * 3, CO_REG_DEST_PTR, cfb);
253 		cyber2000fb_writeb(dst, CO_REG_X_PHASE, cfb);
254 		bgx = ((u32 *)p->dispsw_data)[bgx];
255 		break;
256 
257 	case 32:
258 		bgx = ((u32 *)p->dispsw_data)[bgx];
259 		cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);
260 		break;
261 	}
262 
263 	cyber2000fb_writel(bgx, CO_REG_FOREGROUND, cfb);
264 	cyber2000fb_writew(CO_CMD_L_PATTERN_FGCOL, CO_REG_CMD_L, cfb);
265 	cyber2000fb_writew(0x0800, CO_REG_CMD_H, cfb);
266 }
267 
268 static void
cyber2000_accel_putc(struct vc_data * conp,struct display * p,int c,int yy,int xx)269 cyber2000_accel_putc(struct vc_data *conp, struct display *p, int c,
270 		     int yy, int xx)
271 {
272 	struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
273 
274 	cyber2000_accel_wait(cfb);
275 	cfb->dispsw->putc(conp, p, c, yy, xx);
276 }
277 
278 static void
cyber2000_accel_putcs(struct vc_data * conp,struct display * p,const unsigned short * s,int count,int yy,int xx)279 cyber2000_accel_putcs(struct vc_data *conp, struct display *p,
280 		      const unsigned short *s, int count, int yy, int xx)
281 {
282 	struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
283 
284 	cyber2000_accel_wait(cfb);
285 	cfb->dispsw->putcs(conp, p, s, count, yy, xx);
286 }
287 
cyber2000_accel_revc(struct display * p,int xx,int yy)288 static void cyber2000_accel_revc(struct display *p, int xx, int yy)
289 {
290 	struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
291 
292 	cyber2000_accel_wait(cfb);
293 	cfb->dispsw->revc(p, xx, yy);
294 }
295 
296 static void
cyber2000_accel_clear_margins(struct vc_data * conp,struct display * p,int bottom_only)297 cyber2000_accel_clear_margins(struct vc_data *conp, struct display *p,
298 			      int bottom_only)
299 {
300 	struct cfb_info *cfb = (struct cfb_info *)p->fb_info;
301 
302 	cfb->dispsw->clear_margins(conp, p, bottom_only);
303 }
304 
305 static struct display_switch fbcon_cyber_accel = {
306 	setup:		cyber2000_accel_setup,
307 	bmove:		cyber2000_accel_bmove,
308 	clear:		cyber2000_accel_clear,
309 	putc:		cyber2000_accel_putc,
310 	putcs:		cyber2000_accel_putcs,
311 	revc:		cyber2000_accel_revc,
312 	clear_margins:	cyber2000_accel_clear_margins,
313 	fontwidthmask:	FONTWIDTH(8)|FONTWIDTH(16)
314 };
315 
316 /*
317  *    Set a single color register. Return != 0 for invalid regno.
318  */
319 static int
cyber2000_setcolreg(u_int regno,u_int red,u_int green,u_int blue,u_int transp,struct fb_info * info)320 cyber2000_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
321 		    u_int transp, struct fb_info *info)
322 {
323 	struct cfb_info *cfb = (struct cfb_info *)info;
324 
325 	u_int alpha = transp ^ 0xFFFF;
326 
327 	if (regno >= NR_PALETTE)
328 		return 1;
329 
330 	red   >>= 8;
331 	green >>= 8;
332 	blue  >>= 8;
333 	alpha >>= 8;
334 
335 	cfb->palette[regno].red   = red;
336 	cfb->palette[regno].green = green;
337 	cfb->palette[regno].blue  = blue;
338 
339 	switch (cfb->fb.var.bits_per_pixel) {
340 #ifdef FBCON_HAS_CFB8
341 	case 8:
342 		cyber2000fb_writeb(regno, 0x3c8, cfb);
343 		cyber2000fb_writeb(red,   0x3c9, cfb);
344 		cyber2000fb_writeb(green, 0x3c9, cfb);
345 		cyber2000fb_writeb(blue,  0x3c9, cfb);
346 		break;
347 #endif
348 
349 #ifdef FBCON_HAS_CFB16
350 	case 16:
351 #ifndef CFB16_IS_CFB15
352 		if (regno < 64) {
353 			/* write green */
354 			cyber2000fb_writeb(regno << 2, 0x3c8, cfb);
355 			cyber2000fb_writeb(cfb->palette[regno >> 1].red, 0x3c9, cfb);
356 			cyber2000fb_writeb(green, 0x3c9, cfb);
357 			cyber2000fb_writeb(cfb->palette[regno >> 1].blue, 0x3c9, cfb);
358 		}
359 
360 		if (regno < 32) {
361 			/* write red,blue */
362 			cyber2000fb_writeb(regno << 3, 0x3c8, cfb);
363 			cyber2000fb_writeb(red, 0x3c9, cfb);
364 			cyber2000fb_writeb(cfb->palette[regno << 1].green, 0x3c9, cfb);
365 			cyber2000fb_writeb(blue, 0x3c9, cfb);
366 		}
367 
368 		if (regno < 16)
369 			((u16 *)cfb->fb.pseudo_palette)[regno] =
370 				((red   << 8) & 0xf800) |
371 				((green << 3) & 0x07e0) |
372 				((blue  >> 3));
373 		break;
374 #endif
375 
376 	case 15:
377 		if (regno < 32) {
378 			cyber2000fb_writeb(regno << 3, 0x3c8, cfb);
379 			cyber2000fb_writeb(red, 0x3c9, cfb);
380 			cyber2000fb_writeb(green, 0x3c9, cfb);
381 			cyber2000fb_writeb(blue, 0x3c9, cfb);
382 		}
383 		if (regno < 16)
384 			((u16 *)cfb->fb.pseudo_palette)[regno] =
385 				((red   << 7) & 0x7c00) |
386 				((green << 2) & 0x03e0) |
387 				((blue  >> 3));
388 		break;
389 
390 #endif
391 
392 #ifdef FBCON_HAS_CFB24
393 	case 24:
394 		cyber2000fb_writeb(regno, 0x3c8, cfb);
395 		cyber2000fb_writeb(red,   0x3c9, cfb);
396 		cyber2000fb_writeb(green, 0x3c9, cfb);
397 		cyber2000fb_writeb(blue,  0x3c9, cfb);
398 
399 		if (regno < 16)
400 			((u32 *)cfb->fb.pseudo_palette)[regno] =
401 				(red << 16) | (green << 8) | blue;
402 		break;
403 #endif
404 
405 #ifdef FBCON_HAS_CFB32
406 	case 32:
407 		cyber2000fb_writeb(regno, 0x3c8, cfb);
408 		cyber2000fb_writeb(red,   0x3c9, cfb);
409 		cyber2000fb_writeb(green, 0x3c9, cfb);
410 		cyber2000fb_writeb(blue,  0x3c9, cfb);
411 
412 		if (regno < 16)
413 			((u32 *)cfb->fb.pseudo_palette)[regno] =
414 				(alpha << 24) | (red << 16) | (green << 8) | blue;
415 		break;
416 #endif
417 
418 	default:
419 		return 1;
420 	}
421 
422 	return 0;
423 }
424 
425 struct par_info {
426 	/*
427 	 * Hardware
428 	 */
429 	u_char	clock_mult;
430 	u_char	clock_div;
431 	u_char	visualid;
432 	u_char	pixformat;
433 	u_char	crtc_ofl;
434 	u_char	crtc[19];
435 	u_int	width;
436 	u_int	pitch;
437 	u_int	fetch;
438 
439 	/*
440 	 * Other
441 	 */
442 	u_char	palette_ctrl;
443 	u_int	vmode;
444 };
445 
446 static const u_char crtc_idx[] = {
447 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
448 	0x08, 0x09,
449 	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18
450 };
451 
cyber2000fb_set_timing(struct cfb_info * cfb,struct par_info * hw)452 static void cyber2000fb_set_timing(struct cfb_info *cfb, struct par_info *hw)
453 {
454 	u_int i;
455 
456 	/*
457 	 * Blank palette
458 	 */
459 	for (i = 0; i < NR_PALETTE; i++) {
460 		cyber2000fb_writeb(i, 0x3c8, cfb);
461 		cyber2000fb_writeb(0, 0x3c9, cfb);
462 		cyber2000fb_writeb(0, 0x3c9, cfb);
463 		cyber2000fb_writeb(0, 0x3c9, cfb);
464 	}
465 
466 	cyber2000fb_writeb(0xef, 0x3c2, cfb);
467 	cyber2000_crtcw(0x11, 0x0b, cfb);
468 	cyber2000_attrw(0x11, 0x00, cfb);
469 
470 	cyber2000_seqw(0x00, 0x01, cfb);
471 	cyber2000_seqw(0x01, 0x01, cfb);
472 	cyber2000_seqw(0x02, 0x0f, cfb);
473 	cyber2000_seqw(0x03, 0x00, cfb);
474 	cyber2000_seqw(0x04, 0x0e, cfb);
475 	cyber2000_seqw(0x00, 0x03, cfb);
476 
477 	for (i = 0; i < sizeof(crtc_idx); i++)
478 		cyber2000_crtcw(crtc_idx[i], hw->crtc[i], cfb);
479 
480 	for (i = 0x0a; i < 0x10; i++)
481 		cyber2000_crtcw(i, 0, cfb);
482 
483 	cyber2000_grphw(0x11, hw->crtc_ofl, cfb);
484 	cyber2000_grphw(0x00, 0x00, cfb);
485 	cyber2000_grphw(0x01, 0x00, cfb);
486 	cyber2000_grphw(0x02, 0x00, cfb);
487 	cyber2000_grphw(0x03, 0x00, cfb);
488 	cyber2000_grphw(0x04, 0x00, cfb);
489 	cyber2000_grphw(0x05, 0x60, cfb);
490 	cyber2000_grphw(0x06, 0x05, cfb);
491 	cyber2000_grphw(0x07, 0x0f, cfb);
492 	cyber2000_grphw(0x08, 0xff, cfb);
493 
494 	/* Attribute controller registers */
495 	for (i = 0; i < 16; i++)
496 		cyber2000_attrw(i, i, cfb);
497 
498 	cyber2000_attrw(0x10, 0x01, cfb);
499 	cyber2000_attrw(0x11, 0x00, cfb);
500 	cyber2000_attrw(0x12, 0x0f, cfb);
501 	cyber2000_attrw(0x13, 0x00, cfb);
502 	cyber2000_attrw(0x14, 0x00, cfb);
503 
504 	/* woody: set the interlaced bit... */
505 	/* FIXME: what about doublescan? */
506 	cyber2000fb_writeb(0x11, 0x3ce, cfb);
507 	i = cyber2000fb_readb(0x3cf, cfb);
508 	if (hw->vmode == FB_VMODE_INTERLACED)
509 		i |= 0x20;
510 	else
511 		i &= ~0x20;
512 	cyber2000fb_writeb(i, 0x3cf, cfb);
513 
514 	/* PLL registers */
515 	cyber2000_grphw(DCLK_MULT, hw->clock_mult, cfb);
516 	cyber2000_grphw(DCLK_DIV,  hw->clock_div, cfb);
517 	cyber2000_grphw(MCLK_MULT, cfb->mclk_mult, cfb);
518 	cyber2000_grphw(MCLK_DIV,  cfb->mclk_div, cfb);
519 	cyber2000_grphw(0x90, 0x01, cfb);
520 	cyber2000_grphw(0xb9, 0x80, cfb);
521 	cyber2000_grphw(0xb9, 0x00, cfb);
522 
523 	cyber2000fb_writeb(0x56, 0x3ce, cfb);
524 	i = cyber2000fb_readb(0x3cf, cfb);
525 	cyber2000fb_writeb(i | 4, 0x3cf, cfb);
526 	cyber2000fb_writeb(hw->palette_ctrl, 0x3c6, cfb);
527 	cyber2000fb_writeb(i,    0x3cf, cfb);
528 
529 	cyber2000fb_writeb(0x20, 0x3c0, cfb);
530 	cyber2000fb_writeb(0xff, 0x3c6, cfb);
531 
532 	cyber2000_grphw(0x14, hw->fetch, cfb);
533 	cyber2000_grphw(0x15, ((hw->fetch >> 8) & 0x03) |
534 			      ((hw->pitch >> 4) & 0x30), cfb);
535 	cyber2000_grphw(0x77, hw->visualid, cfb);
536 
537 	/* make sure we stay in linear mode */
538 	cyber2000_grphw(0x33, 0x0d, cfb);
539 
540 	/*
541 	 * Set up accelerator registers
542 	 */
543 	cyber2000fb_writew(hw->width,     CO_REG_SRC_WIDTH,  cfb);
544 	cyber2000fb_writew(hw->width,     CO_REG_DEST_WIDTH, cfb);
545 	cyber2000fb_writeb(hw->pixformat, CO_REG_PIX_FORMAT, cfb);
546 }
547 
548 static inline int
cyber2000fb_update_start(struct cfb_info * cfb,struct fb_var_screeninfo * var)549 cyber2000fb_update_start(struct cfb_info *cfb, struct fb_var_screeninfo *var)
550 {
551 	u_int base;
552 
553 	base = var->yoffset * var->xres_virtual + var->xoffset;
554 
555 	/* have to be careful, because bits_per_pixel might be 15
556 	   in this version of the driver -- dok@directfb.org 2002/06/13 */
557 	base *= (var->bits_per_pixel + 7) >> 3;
558 
559 	base >>= 2;
560 
561 	if (base >= 1 << 20)
562 		return -EINVAL;
563 
564 	cyber2000_grphw(0x10, base >> 16 | 0x10, cfb);
565 	cyber2000_crtcw(0x0c, base >> 8, cfb);
566 	cyber2000_crtcw(0x0d, base, cfb);
567 
568 	return 0;
569 }
570 
571 /*
572  * Set the Colormap
573  */
574 static int
cyber2000fb_set_cmap(struct fb_cmap * cmap,int kspc,int con,struct fb_info * info)575 cyber2000fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
576 		     struct fb_info *info)
577 {
578 	struct cfb_info *cfb = (struct cfb_info *)info;
579 	struct fb_cmap *dcmap = &fb_display[con].cmap;
580 	int err = 0;
581 
582 	/* no colormap allocated? */
583 	if (!dcmap->len) {
584 		int size;
585 
586 		if (cfb->fb.var.bits_per_pixel == 16)
587 			size = 32;
588 		else
589 			size = 256;
590 
591 		err = fb_alloc_cmap(dcmap, size, 0);
592 	}
593 
594 	/*
595 	 * we should be able to remove this test once fbcon has been
596 	 * "improved" --rmk
597 	 */
598 	if (!err && con == cfb->currcon) {
599 		err = fb_set_cmap(cmap, kspc, cyber2000_setcolreg, &cfb->fb);
600 		dcmap = &cfb->fb.cmap;
601 	}
602 
603 	if (!err)
604 		fb_copy_cmap(cmap, dcmap, kspc ? 0 : 1);
605 
606 	return err;
607 }
608 
609 static int
cyber2000fb_decode_crtc(struct par_info * hw,struct cfb_info * cfb,struct fb_var_screeninfo * var)610 cyber2000fb_decode_crtc(struct par_info *hw, struct cfb_info *cfb,
611 			struct fb_var_screeninfo *var)
612 {
613 	u_int Htotal, Hblankend, Hsyncend;
614 	u_int Vtotal, Vdispend, Vblankstart, Vblankend, Vsyncstart, Vsyncend;
615 #define BIT(v,b1,m,b2) (((v >> b1) & m) << b2)
616 
617 	hw->crtc[13] = hw->pitch;
618 	hw->crtc[17] = 0xe3;
619 	hw->crtc[14] = 0;
620 	hw->crtc[8]  = 0;
621 
622 	Htotal      = var->xres + var->right_margin +
623 		      var->hsync_len + var->left_margin;
624 
625 	if (Htotal > 2080)
626 		return -EINVAL;
627 
628 	hw->crtc[0] = (Htotal >> 3) - 5;
629 	hw->crtc[1] = (var->xres >> 3) - 1;
630 	hw->crtc[2] = var->xres >> 3;
631 	hw->crtc[4] = (var->xres + var->right_margin) >> 3;
632 
633 	Hblankend   = (Htotal - 4*8) >> 3;
634 
635 	hw->crtc[3] = BIT(Hblankend,  0, 0x1f,  0) |
636 		      BIT(1,          0, 0x01,  7);
637 
638 	Hsyncend    = (var->xres + var->right_margin + var->hsync_len) >> 3;
639 
640 	hw->crtc[5] = BIT(Hsyncend,   0, 0x1f,  0) |
641 		      BIT(Hblankend,  5, 0x01,  7);
642 
643 	Vdispend    = var->yres - 1;
644 	Vsyncstart  = var->yres + var->lower_margin;
645 	Vsyncend    = var->yres + var->lower_margin + var->vsync_len;
646 	Vtotal      = var->yres + var->lower_margin + var->vsync_len +
647 		      var->upper_margin - 2;
648 
649 	if (Vtotal > 2047)
650 		return -EINVAL;
651 
652 	Vblankstart = var->yres + 6;
653 	Vblankend   = Vtotal - 10;
654 
655 	hw->crtc[6]  = Vtotal;
656 	hw->crtc[7]  = BIT(Vtotal,     8, 0x01,  0) |
657 			BIT(Vdispend,   8, 0x01,  1) |
658 			BIT(Vsyncstart, 8, 0x01,  2) |
659 			BIT(Vblankstart,8, 0x01,  3) |
660 			BIT(1,          0, 0x01,  4) |
661 	        	BIT(Vtotal,     9, 0x01,  5) |
662 			BIT(Vdispend,   9, 0x01,  6) |
663 			BIT(Vsyncstart, 9, 0x01,  7);
664 	hw->crtc[9]  = BIT(0,          0, 0x1f,  0) |
665 		        BIT(Vblankstart,9, 0x01,  5) |
666 			BIT(1,          0, 0x01,  6);
667 	hw->crtc[10] = Vsyncstart;
668 	hw->crtc[11] = BIT(Vsyncend,   0, 0x0f,  0) |
669 		       BIT(1,          0, 0x01,  7);
670 	hw->crtc[12] = Vdispend;
671 	hw->crtc[15] = Vblankstart;
672 	hw->crtc[16] = Vblankend;
673 	hw->crtc[18] = 0xff;
674 
675 	/* overflow - graphics reg 0x11 */
676 	/* 0=VTOTAL:10 1=VDEND:10 2=VRSTART:10 3=VBSTART:10
677 	 * 4=LINECOMP:10 5-IVIDEO 6=FIXCNT
678 	 */
679 	hw->crtc_ofl =
680 		BIT(Vtotal,     10, 0x01,  0) |
681 		BIT(Vdispend,   10, 0x01,  1) |
682 		BIT(Vsyncstart, 10, 0x01,  2) |
683 		BIT(Vblankstart,10, 0x01,  3) |
684 		1 << 4;
685 
686 	return 0;
687 }
688 
689 /*
690  * The following was discovered by a good monitor, bit twiddling, theorising
691  * and but mostly luck.  Strangely, it looks like everyone elses' PLL!
692  *
693  * Clock registers:
694  *   fclock = fpll / div2
695  *   fpll   = fref * mult / div1
696  * where:
697  *   fref = 14.318MHz (69842ps)
698  *   mult = reg0xb0.7:0
699  *   div1 = (reg0xb1.5:0 + 1)
700  *   div2 =  2^(reg0xb1.7:6)
701  *   fpll should be between 115 and 260 MHz
702  *  (8696ps and 3846ps)
703  */
704 static int
cyber2000fb_decode_clock(struct par_info * hw,struct cfb_info * cfb,struct fb_var_screeninfo * var)705 cyber2000fb_decode_clock(struct par_info *hw, struct cfb_info *cfb,
706 			 struct fb_var_screeninfo *var)
707 {
708 	u_long pll_ps = var->pixclock;
709 	const u_long ref_ps = cfb->ref_ps;
710 	u_int div2, t_div1, best_div1, best_mult;
711 	int best_diff;
712 	int vco;
713 
714 	/*
715 	 * Step 1:
716 	 *   find div2 such that 115MHz < fpll < 260MHz
717 	 *   and 0 <= div2 < 4
718 	 */
719 	for (div2 = 0; div2 < 4; div2++) {
720 		u_long new_pll;
721 
722 		new_pll = pll_ps / cfb->divisors[div2];
723 		if (8696 > new_pll && new_pll > 3846) {
724 			pll_ps = new_pll;
725 			break;
726 		}
727 	}
728 
729 	if (div2 == 4)
730 		return -EINVAL;
731 
732 	/*
733 	 * Step 2:
734 	 *  Given pll_ps and ref_ps, find:
735 	 *    pll_ps * 0.995 < pll_ps_calc < pll_ps * 1.005
736 	 *  where { 1 < best_div1 < 32, 1 < best_mult < 256 }
737 	 *    pll_ps_calc = best_div1 / (ref_ps * best_mult)
738 	 */
739 	best_diff = 0x7fffffff;
740 	best_mult = 32;
741 	best_div1 = 255;
742 	for (t_div1 = 32; t_div1 > 1; t_div1 -= 1) {
743 		u_int rr, t_mult, t_pll_ps;
744 		int diff;
745 
746 		/*
747 		 * Find the multiplier for this divisor
748 		 */
749 		rr = ref_ps * t_div1;
750 		t_mult = (rr + pll_ps / 2) / pll_ps;
751 
752 		/*
753 		 * Is the multiplier within the correct range?
754 		 */
755 		if (t_mult > 256 || t_mult < 2)
756 			continue;
757 
758 		/*
759 		 * Calculate the actual clock period from this multiplier
760 		 * and divisor, and estimate the error.
761 		 */
762 		t_pll_ps = (rr + t_mult / 2) / t_mult;
763 		diff = pll_ps - t_pll_ps;
764 		if (diff < 0)
765 			diff = -diff;
766 
767 		if (diff < best_diff) {
768 			best_diff = diff;
769 			best_mult = t_mult;
770 			best_div1 = t_div1;
771 		}
772 
773 		/*
774 		 * If we hit an exact value, there is no point in continuing.
775 		 */
776 		if (diff == 0)
777 			break;
778 	}
779 
780 	/*
781 	 * Step 3:
782 	 *  combine values
783 	 */
784 	hw->clock_mult = best_mult - 1;
785 	hw->clock_div  = div2 << 6 | (best_div1 - 1);
786 
787 	vco = ref_ps * best_div1 / best_mult;
788 	if ((ref_ps == 40690) && (vco < 5556))
789 		/* Set VFSEL when VCO > 180MHz (5.556 ps). */
790 		hw->clock_div |= DCLK_DIV_VFSEL;
791 
792 	return 0;
793 }
794 
795 /*
796  * Decode the info required for the hardware.
797  * This involves the PLL parameters for the dot clock,
798  * CRTC registers, and accelerator settings.
799  */
800 static int
cyber2000fb_decode_var(struct fb_var_screeninfo * var,struct cfb_info * cfb,struct par_info * hw)801 cyber2000fb_decode_var(struct fb_var_screeninfo *var, struct cfb_info *cfb,
802 		       struct par_info *hw)
803 {
804 	int err;
805 
806 	hw->width = var->xres_virtual;
807 	hw->palette_ctrl = 0x06;
808 	hw->vmode = var->vmode;
809 
810 	switch (var->bits_per_pixel) {
811 #ifdef FBCON_HAS_CFB8
812 	case 8:	/* PSEUDOCOLOUR, 256 */
813 		hw->pixformat		= PIXFORMAT_8BPP;
814 		hw->visualid		= VISUALID_256;
815 		hw->pitch		= hw->width >> 3;
816 		break;
817 #endif
818 #ifdef FBCON_HAS_CFB16
819 	case 16:/* DIRECTCOLOUR, 64k */
820 #ifndef CFB16_IS_CFB15
821 		hw->pixformat		= PIXFORMAT_16BPP;
822 		hw->visualid		= VISUALID_64K;
823 		hw->pitch		= hw->width >> 2;
824 		hw->palette_ctrl	|= 0x10;
825 		break;
826 #endif
827 	case 15:/* DIRECTCOLOUR, 32k */
828 		hw->pixformat		= PIXFORMAT_16BPP;
829 		hw->visualid		= VISUALID_32K;
830 		hw->pitch		= hw->width >> 2;
831 		hw->palette_ctrl	|= 0x10;
832 		break;
833 
834 #endif
835 #ifdef FBCON_HAS_CFB24
836 	case 24:/* TRUECOLOUR, 16m */
837 		hw->pixformat		= PIXFORMAT_24BPP;
838 		hw->visualid		= VISUALID_16M;
839 		hw->width		*= 3;
840 		hw->pitch		= hw->width >> 3;
841 		hw->palette_ctrl	|= 0x10;
842 		break;
843 #endif
844 #ifdef FBCON_HAS_CFB32
845 	case 32:/* TRUECOLOUR, 16m */
846 		hw->pixformat		= PIXFORMAT_32BPP;
847 		hw->visualid		= VISUALID_16M_32;
848 		hw->pitch		= hw->width >> 1;
849 		hw->palette_ctrl	|= 0x10;
850 		break;
851 #endif
852 	default:
853 		return -EINVAL;
854 	}
855 
856 	err = cyber2000fb_decode_clock(hw, cfb, var);
857 	if (err)
858 		return err;
859 
860 	err = cyber2000fb_decode_crtc(hw, cfb, var);
861 	if (err)
862 		return err;
863 
864 	hw->width -= 1;
865 	hw->fetch = hw->pitch;
866 	if (!(cfb->mem_ctl2 & MEM_CTL2_64BIT))
867 		hw->fetch <<= 1;
868 	hw->fetch += 1;
869 
870 	return 0;
871 }
872 
873 /*
874  *    Set the User Defined Part of the Display
875  */
876 static int
cyber2000fb_set_var(struct fb_var_screeninfo * var,int con,struct fb_info * info)877 cyber2000fb_set_var(struct fb_var_screeninfo *var, int con,
878 		    struct fb_info *info)
879 {
880 	struct cfb_info *cfb = (struct cfb_info *)info;
881 	struct display *display;
882 	struct par_info hw;
883 	int err, chgvar = 0;
884 
885 	/*
886 	 * CONUPDATE and SMOOTH_XPAN are equal.  However,
887 	 * SMOOTH_XPAN is only used internally by fbcon.
888 	 */
889 	if (var->vmode & FB_VMODE_CONUPDATE) {
890 		var->vmode |= FB_VMODE_YWRAP;
891 		var->xoffset = cfb->fb.var.xoffset;
892 		var->yoffset = cfb->fb.var.yoffset;
893 	}
894 
895 	err = cyber2000fb_decode_var(var, (struct cfb_info *)info, &hw);
896 	if (err)
897 		return err;
898 
899 	if (var->activate & FB_ACTIVATE_TEST)
900 		return 0;
901 
902 	if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
903 		return -EINVAL;
904 
905 	if (cfb->fb.var.xres != var->xres)
906 		chgvar = 1;
907 	if (cfb->fb.var.yres != var->yres)
908 		chgvar = 1;
909 	if (cfb->fb.var.xres_virtual != var->xres_virtual)
910 		chgvar = 1;
911 	if (cfb->fb.var.yres_virtual != var->yres_virtual)
912 		chgvar = 1;
913 	if (cfb->fb.var.bits_per_pixel != var->bits_per_pixel)
914 		chgvar = 1;
915 
916 	if (con < 0) {
917 		display = cfb->fb.disp;
918 		chgvar = 0;
919 	} else {
920 		display = fb_display + con;
921 	}
922 
923 	var->red.msb_right	= 0;
924 	var->green.msb_right	= 0;
925 	var->blue.msb_right	= 0;
926 
927 	switch (var->bits_per_pixel) {
928 #ifdef FBCON_HAS_CFB8
929 	case 8:	/* PSEUDOCOLOUR, 256 */
930 		var->red.offset		= 0;
931 		var->red.length		= 8;
932 		var->green.offset	= 0;
933 		var->green.length	= 8;
934 		var->blue.offset	= 0;
935 		var->blue.length	= 8;
936 
937 		cfb->fb.fix.visual	= FB_VISUAL_PSEUDOCOLOR;
938 		cfb->dispsw		= &fbcon_cfb8;
939 		display->dispsw_data	= NULL;
940 		display->next_line	= var->xres_virtual;
941 		break;
942 #endif
943 #ifdef FBCON_HAS_CFB16
944 	case 16:/* DIRECTCOLOUR, 64k */
945 #ifndef CFB16_IS_CFB15
946 		var->red.offset		= 11;
947 		var->red.length		= 5;
948 		var->green.offset	= 5;
949 		var->green.length	= 6;
950 		var->blue.offset	= 0;
951 		var->blue.length	= 5;
952 
953 		cfb->fb.fix.visual	= FB_VISUAL_DIRECTCOLOR;
954 		cfb->dispsw		= &fbcon_cfb16;
955 		display->dispsw_data	= cfb->fb.pseudo_palette;
956 		display->next_line	= var->xres_virtual * 2;
957 		break;
958 #endif
959 	case 15:/* DIRECTCOLOUR, 32k */
960 		var->bits_per_pixel	= 15;
961 		var->red.offset		= 10;
962 		var->red.length		= 5;
963 		var->green.offset	= 5;
964 		var->green.length	= 5;
965 		var->blue.offset	= 0;
966 		var->blue.length	= 5;
967 
968 		cfb->fb.fix.visual	= FB_VISUAL_DIRECTCOLOR;
969 		cfb->dispsw		= &fbcon_cfb16;
970 		display->dispsw_data	= cfb->fb.pseudo_palette;
971 		display->next_line	= var->xres_virtual * 2;
972 		break;
973 #endif
974 #ifdef FBCON_HAS_CFB24
975 	case 24:/* TRUECOLOUR, 16m */
976 		var->red.offset		= 16;
977 		var->red.length		= 8;
978 		var->green.offset	= 8;
979 		var->green.length	= 8;
980 		var->blue.offset	= 0;
981 		var->blue.length	= 8;
982 
983 		cfb->fb.fix.visual	= FB_VISUAL_TRUECOLOR;
984 		cfb->dispsw		= &fbcon_cfb24;
985 		display->dispsw_data	= cfb->fb.pseudo_palette;
986 		display->next_line	= var->xres_virtual * 3;
987 		break;
988 #endif
989 #ifdef FBCON_HAS_CFB32
990 	case 32:/* TRUECOLOUR, 16m */
991 		var->transp.offset	= 24;
992 		var->transp.length	= 8;
993 		var->red.offset		= 16;
994 		var->red.length		= 8;
995 		var->green.offset	= 8;
996 		var->green.length	= 8;
997 		var->blue.offset	= 0;
998 		var->blue.length	= 8;
999 
1000 		cfb->fb.fix.visual	= FB_VISUAL_TRUECOLOR;
1001 		cfb->dispsw		= &fbcon_cfb32;
1002 		display->dispsw_data	= cfb->fb.pseudo_palette;
1003 		display->next_line	= var->xres_virtual * 4;
1004 		break;
1005 #endif
1006 	default:/* in theory this should never happen */
1007 		printk(KERN_WARNING "%s: no support for %dbpp\n",
1008 		       cfb->fb.fix.id, var->bits_per_pixel);
1009 		cfb->dispsw = &fbcon_dummy;
1010 		break;
1011 	}
1012 
1013 	if (var->accel_flags & FB_ACCELF_TEXT && cfb->dispsw != &fbcon_dummy)
1014 		display->dispsw = &fbcon_cyber_accel;
1015 	else
1016 		display->dispsw = cfb->dispsw;
1017 
1018 	cfb->fb.fix.line_length	= display->next_line;
1019 
1020 	display->screen_base	= cfb->fb.screen_base;
1021 	display->line_length	= cfb->fb.fix.line_length;
1022 	display->visual		= cfb->fb.fix.visual;
1023 	display->type		= cfb->fb.fix.type;
1024 	display->type_aux	= cfb->fb.fix.type_aux;
1025 	display->ypanstep	= cfb->fb.fix.ypanstep;
1026 	display->ywrapstep	= cfb->fb.fix.ywrapstep;
1027 	display->can_soft_blank = 1;
1028 	display->inverse	= 0;
1029 
1030 	cfb->fb.var = *var;
1031 	cfb->fb.var.activate &= ~FB_ACTIVATE_ALL;
1032 
1033 	/*
1034 	 * Update the old var.  The fbcon drivers still use this.
1035 	 * Once they are using cfb->fb.var, this can be dropped.
1036 	 *					--rmk
1037 	 */
1038 	display->var = cfb->fb.var;
1039 
1040 	/*
1041 	 * If we are setting all the virtual consoles, also set the
1042 	 * defaults used to create new consoles.
1043 	 */
1044 	if (var->activate & FB_ACTIVATE_ALL)
1045 		cfb->fb.disp->var = cfb->fb.var;
1046 
1047 	if (chgvar && info && cfb->fb.changevar)
1048 		cfb->fb.changevar(con);
1049 
1050 	cyber2000fb_update_start(cfb, var);
1051 	cyber2000fb_set_timing(cfb, &hw);
1052 	fb_set_cmap(&cfb->fb.cmap, 1, cyber2000_setcolreg, &cfb->fb);
1053 
1054 	return 0;
1055 }
1056 
1057 
1058 /*
1059  *    Pan or Wrap the Display
1060  */
1061 static int
cyber2000fb_pan_display(struct fb_var_screeninfo * var,int con,struct fb_info * info)1062 cyber2000fb_pan_display(struct fb_var_screeninfo *var, int con,
1063 			struct fb_info *info)
1064 {
1065 	struct cfb_info *cfb = (struct cfb_info *)info;
1066 	u_int y_bottom;
1067 
1068 	y_bottom = var->yoffset;
1069 
1070 	if (!(var->vmode & FB_VMODE_YWRAP))
1071 		y_bottom += var->yres;
1072 
1073 	if (var->xoffset > (var->xres_virtual - var->xres))
1074 		return -EINVAL;
1075 	if (y_bottom > cfb->fb.var.yres_virtual)
1076 		return -EINVAL;
1077 
1078 	if (cyber2000fb_update_start(cfb, var))
1079 		return -EINVAL;
1080 
1081 	cfb->fb.var.xoffset = var->xoffset;
1082 	cfb->fb.var.yoffset = var->yoffset;
1083 	if (var->vmode & FB_VMODE_YWRAP) {
1084 		cfb->fb.var.vmode |= FB_VMODE_YWRAP;
1085 	} else {
1086 		cfb->fb.var.vmode &= ~FB_VMODE_YWRAP;
1087 	}
1088 
1089 	return 0;
1090 }
1091 
1092 
1093 /*
1094  *    Update the `var' structure (called by fbcon.c)
1095  *
1096  *    This call looks only at yoffset and the FB_VMODE_YWRAP flag in `var'.
1097  *    Since it's called by a kernel driver, no range checking is done.
1098  */
cyber2000fb_updatevar(int con,struct fb_info * info)1099 static int cyber2000fb_updatevar(int con, struct fb_info *info)
1100 {
1101 	struct cfb_info *cfb = (struct cfb_info *)info;
1102 
1103 	return cyber2000fb_update_start(cfb, &fb_display[con].var);
1104 }
1105 
cyber2000fb_switch(int con,struct fb_info * info)1106 static int cyber2000fb_switch(int con, struct fb_info *info)
1107 {
1108 	struct cfb_info *cfb = (struct cfb_info *)info;
1109 	struct display *disp;
1110 	struct fb_cmap *cmap;
1111 
1112 	if (cfb->currcon >= 0) {
1113 		disp = fb_display + cfb->currcon;
1114 
1115 		/*
1116 		 * Save the old colormap and video mode.
1117 		 */
1118 		disp->var = cfb->fb.var;
1119 		if (disp->cmap.len)
1120 			fb_copy_cmap(&cfb->fb.cmap, &disp->cmap, 0);
1121 	}
1122 
1123 	cfb->currcon = con;
1124 	disp = fb_display + con;
1125 
1126 	/*
1127 	 * Install the new colormap and change the video mode.  By default,
1128 	 * fbcon sets all the colormaps and video modes to the default
1129 	 * values at bootup.
1130 	 *
1131 	 * Really, we want to set the colourmap size depending on the
1132 	 * depth of the new video mode.  For now, we leave it at its
1133 	 * default 256 entry.
1134 	 */
1135 	if (disp->cmap.len)
1136 		cmap = &disp->cmap;
1137 	else
1138 		cmap = fb_default_cmap(1 << disp->var.bits_per_pixel);
1139 
1140 	fb_copy_cmap(cmap, &cfb->fb.cmap, 0);
1141 
1142 	cfb->fb.var = disp->var;
1143 	cfb->fb.var.activate = FB_ACTIVATE_NOW;
1144 
1145 	cyber2000fb_set_var(&cfb->fb.var, con, &cfb->fb);
1146 
1147 	return 0;
1148 }
1149 
1150 /*
1151  *    (Un)Blank the display.
1152  */
cyber2000fb_blank(int blank,struct fb_info * info)1153 static void cyber2000fb_blank(int blank, struct fb_info *info)
1154 {
1155 	struct cfb_info *cfb = (struct cfb_info *)info;
1156 	int i;
1157 
1158 	/*
1159 	 *  Blank the screen if blank_mode != 0, else unblank. If
1160 	 *  blank == NULL then the caller blanks by setting the CLUT
1161 	 *  (Color Look Up Table) to all black. Return 0 if blanking
1162 	 *  succeeded, != 0 if un-/blanking failed due to e.g. a
1163 	 *  video mode which doesn't support it. Implements VESA
1164 	 *  suspend and powerdown modes on hardware that supports
1165 	 *  disabling hsync/vsync:
1166 	 *    blank_mode == 2: suspend vsync
1167 	 *    blank_mode == 3: suspend hsync
1168 	 *    blank_mode == 4: powerdown
1169 	 *
1170 	 *  wms...Enable VESA DMPS compatible powerdown mode
1171 	 *  run "setterm -powersave powerdown" to take advantage
1172 	 */
1173 
1174 	switch (blank) {
1175 	case 4:	/* powerdown - both sync lines down */
1176     		cyber2000_grphw(0x16, 0x05, cfb);
1177 		break;
1178 	case 3:	/* hsync off */
1179     		cyber2000_grphw(0x16, 0x01, cfb);
1180 		break;
1181 	case 2:	/* vsync off */
1182     		cyber2000_grphw(0x16, 0x04, cfb);
1183 		break;
1184 	case 1:	/* soft blank */
1185 		cyber2000_grphw(0x16, 0x00, cfb);
1186 		for (i = 0; i < NR_PALETTE; i++) {
1187 			cyber2000fb_writeb(i, 0x3c8, cfb);
1188 			cyber2000fb_writeb(0, 0x3c9, cfb);
1189 			cyber2000fb_writeb(0, 0x3c9, cfb);
1190 			cyber2000fb_writeb(0, 0x3c9, cfb);
1191 		}
1192 		break;
1193 	default: /* unblank */
1194 		cyber2000_grphw(0x16, 0x00, cfb);
1195 		for (i = 0; i < NR_PALETTE; i++) {
1196 			cyber2000fb_writeb(i, 0x3c8, cfb);
1197 			cyber2000fb_writeb(cfb->palette[i].red, 0x3c9, cfb);
1198 			cyber2000fb_writeb(cfb->palette[i].green, 0x3c9, cfb);
1199 			cyber2000fb_writeb(cfb->palette[i].blue, 0x3c9, cfb);
1200 		}
1201 		break;
1202 	}
1203 }
1204 
1205 /*
1206  * Get the currently displayed virtual consoles colormap.
1207  */
1208 static int
gen_get_cmap(struct fb_cmap * cmap,int kspc,int con,struct fb_info * info)1209 gen_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
1210 {
1211 	fb_copy_cmap(&info->cmap, cmap, kspc ? 0 : 2);
1212 	return 0;
1213 }
1214 
1215 /*
1216  * Get the currently displayed virtual consoles fixed part of the display.
1217  */
1218 static int
gen_get_fix(struct fb_fix_screeninfo * fix,int con,struct fb_info * info)1219 gen_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
1220 {
1221 	*fix = info->fix;
1222 	return 0;
1223 }
1224 
1225 /*
1226  * Get the current user defined part of the display.
1227  */
1228 static int
gen_get_var(struct fb_var_screeninfo * var,int con,struct fb_info * info)1229 gen_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
1230 {
1231 	*var = info->var;
1232 	return 0;
1233 }
1234 
1235 static struct fb_ops cyber2000fb_ops = {
1236 	owner:		THIS_MODULE,
1237 	fb_set_var:	cyber2000fb_set_var,
1238 	fb_set_cmap:	cyber2000fb_set_cmap,
1239 	fb_pan_display:	cyber2000fb_pan_display,
1240 	fb_get_fix:	gen_get_fix,
1241 	fb_get_var:	gen_get_var,
1242 	fb_get_cmap:	gen_get_cmap,
1243 };
1244 
1245 /*
1246  * Enable access to the extended registers
1247  */
cyber2000fb_enable_extregs(struct cfb_info * cfb)1248 static void cyber2000fb_enable_extregs(struct cfb_info *cfb)
1249 {
1250 	cfb->func_use_count += 1;
1251 
1252 	if (cfb->func_use_count == 1) {
1253 		int old;
1254 
1255 		old = cyber2000_grphr(FUNC_CTL, cfb);
1256 		cyber2000_grphw(FUNC_CTL, old | FUNC_CTL_EXTREGENBL, cfb);
1257 	}
1258 }
1259 
1260 /*
1261  * Disable access to the extended registers
1262  */
cyber2000fb_disable_extregs(struct cfb_info * cfb)1263 static void cyber2000fb_disable_extregs(struct cfb_info *cfb)
1264 {
1265 	if (cfb->func_use_count == 1) {
1266 		int old;
1267 
1268 		old = cyber2000_grphr(FUNC_CTL, cfb);
1269 		cyber2000_grphw(FUNC_CTL, old & ~FUNC_CTL_EXTREGENBL, cfb);
1270 	}
1271 
1272 	cfb->func_use_count -= 1;
1273 }
1274 
1275 /*
1276  * This is the only "static" reference to the internal data structures
1277  * of this driver.  It is here solely at the moment to support the other
1278  * CyberPro modules external to this driver.
1279  */
1280 static struct cfb_info		*int_cfb_info;
1281 
1282 /*
1283  * Attach a capture/tv driver to the core CyberX0X0 driver.
1284  */
cyber2000fb_attach(struct cyberpro_info * info,int idx)1285 int cyber2000fb_attach(struct cyberpro_info *info, int idx)
1286 {
1287 	if (int_cfb_info != NULL) {
1288 		info->dev	      = int_cfb_info->dev;
1289 		info->regs	      = int_cfb_info->regs;
1290 		info->fb	      = int_cfb_info->fb.screen_base;
1291 		info->fb_size	      = int_cfb_info->fb.fix.smem_len;
1292 		info->enable_extregs  = cyber2000fb_enable_extregs;
1293 		info->disable_extregs = cyber2000fb_disable_extregs;
1294 		info->info            = int_cfb_info;
1295 
1296 		strncpy(info->dev_name, int_cfb_info->fb.fix.id, sizeof(info->dev_name));
1297 
1298 		MOD_INC_USE_COUNT;
1299 	}
1300 
1301 	return int_cfb_info != NULL;
1302 }
1303 
1304 /*
1305  * Detach a capture/tv driver from the core CyberX0X0 driver.
1306  */
cyber2000fb_detach(int idx)1307 void cyber2000fb_detach(int idx)
1308 {
1309 	MOD_DEC_USE_COUNT;
1310 }
1311 
1312 EXPORT_SYMBOL(cyber2000fb_attach);
1313 EXPORT_SYMBOL(cyber2000fb_detach);
1314 
1315 /*
1316  * These parameters give
1317  * 640x480, hsync 31.5kHz, vsync 60Hz
1318  */
1319 static struct fb_videomode __devinitdata cyber2000fb_default_mode = {
1320 	refresh:	60,
1321 	xres:		640,
1322 	yres:		480,
1323 	pixclock:	39722,
1324 	left_margin:	56,
1325 	right_margin:	16,
1326 	upper_margin:	34,
1327 	lower_margin:	9,
1328 	hsync_len:	88,
1329 	vsync_len:	2,
1330 	sync:		FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
1331 	vmode:		FB_VMODE_NONINTERLACED
1332 };
1333 
1334 static char igs_regs[] __devinitdata = {
1335 					0x12, 0x00,	0x13, 0x00,
1336 					0x16, 0x00,
1337 			0x31, 0x00,	0x32, 0x00,
1338 	0x50, 0x00,	0x51, 0x00,	0x52, 0x00,	0x53, 0x00,
1339 	0x54, 0x00,	0x55, 0x00,	0x56, 0x00,	0x57, 0x01,
1340 	0x58, 0x00,	0x59, 0x00,	0x5a, 0x00,
1341 	0x70, 0x0b,					0x73, 0x30,
1342 	0x74, 0x0b,	0x75, 0x17,	0x76, 0x00,	0x7a, 0xc8
1343 };
1344 
1345 /*
1346  * We need to wake up the CyberPro, and make sure its in linear memory
1347  * mode.  Unfortunately, this is specific to the platform and card that
1348  * we are running on.
1349  *
1350  * On x86 and ARM, should we be initialising the CyberPro first via the
1351  * IO registers, and then the MMIO registers to catch all cases?  Can we
1352  * end up in the situation where the chip is in MMIO mode, but not awake
1353  * on an x86 system?
1354  *
1355  * Note that on the NetWinder, the firmware automatically detects the
1356  * type, width and size, and leaves this in extended registers 0x71 and
1357  * 0x72 for us.
1358  */
cyberpro_init_hw(struct cfb_info * cfb,int at_boot)1359 static inline void cyberpro_init_hw(struct cfb_info *cfb, int at_boot)
1360 {
1361 	int i;
1362 
1363 	/*
1364 	 * Wake up the CyberPro.
1365 	 */
1366 #ifdef __sparc__
1367 #ifdef __sparc_v9__
1368 #error "You loose, consult DaveM."
1369 #else
1370 	/*
1371 	 * SPARC does not have an "outb" instruction, so we generate
1372 	 * I/O cycles storing into a reserved memory space at
1373 	 * physical address 0x3000000
1374 	 */
1375 	{
1376 		unsigned char *iop;
1377 
1378 		iop = ioremap(0x3000000, 0x5000);
1379 		if (iop == NULL) {
1380 			prom_printf("iga5000: cannot map I/O\n");
1381 			return -ENOMEM;
1382 		}
1383 
1384 		writeb(0x18, iop + 0x46e8);
1385 		writeb(0x01, iop + 0x102);
1386 		writeb(0x08, iop + 0x46e8);
1387 		writeb(0x33, iop + 0x3ce);
1388 		writeb(0x01, iop + 0x3cf);
1389 
1390 		iounmap((void *)iop);
1391 	}
1392 #endif
1393 
1394 	if (at_boot) {
1395 		/*
1396 		 * Use mclk from BIOS.  Only read this if we're
1397 		 * initialising this card for the first time.
1398 		 * FIXME: what about hotplug?
1399 		 */
1400 		cfb->mclk_mult = cyber2000_grphr(MCLK_MULT, cfb);
1401 		cfb->mclk_div  = cyber2000_grphr(MCLK_DIV, cfb);
1402 	}
1403 #endif
1404 #if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
1405 	/*
1406 	 * x86 and MIPS are simple, we just do regular
1407 	 * outb's instead of cyber2000fb_writeb.
1408 	 */
1409 	outb(0x18, 0x46e8);
1410 	outb(0x01, 0x102);
1411 	outb(0x08, 0x46e8);
1412 	outb(0x33, 0x3ce);
1413 	outb(0x01, 0x3cf);
1414 
1415 	if (at_boot) {
1416 		/*
1417 		 * Use mclk from BIOS.  Only read this if we're
1418 		 * initialising this card for the first time.
1419 		 * FIXME: what about hotplug?
1420 		 */
1421 		cfb->mclk_mult = cyber2000_grphr(MCLK_MULT, cfb);
1422 		cfb->mclk_div  = cyber2000_grphr(MCLK_DIV, cfb);
1423 	}
1424 #endif
1425 #ifdef __arm__
1426 	cyber2000fb_writeb(0x18, 0x46e8, cfb);
1427 	cyber2000fb_writeb(0x01, 0x102, cfb);
1428 	cyber2000fb_writeb(0x08, 0x46e8, cfb);
1429 	cyber2000fb_writeb(0x33, 0x3ce, cfb);
1430 	cyber2000fb_writeb(0x01, 0x3cf, cfb);
1431 
1432 	/*
1433 	 * MCLK on the NetWinder and the Shark is fixed at 75MHz
1434 	 */
1435 	cfb->mclk_mult = 0xdb;
1436 	cfb->mclk_div  = 0x54;
1437 #endif
1438 
1439 	/*
1440 	 * Initialise the CyberPro
1441 	 */
1442 	for (i = 0; i < sizeof(igs_regs); i += 2)
1443 		cyber2000_grphw(igs_regs[i], igs_regs[i+1], cfb);
1444 
1445 	if (at_boot) {
1446 		/*
1447 		 * get the video RAM size and width from the VGA register.
1448 		 * This should have been already initialised by the BIOS,
1449 		 * but if it's garbage, claim default 1MB VRAM (woody)
1450 		 */
1451 		cfb->mem_ctl1 = cyber2000_grphr(MEM_CTL1, cfb);
1452 		cfb->mem_ctl2 = cyber2000_grphr(MEM_CTL2, cfb);
1453 	} else {
1454 		/*
1455 		 * Reprogram the MEM_CTL1 and MEM_CTL2 registers
1456 		 */
1457 		cyber2000_grphw(MEM_CTL1, cfb->mem_ctl1, cfb);
1458 		cyber2000_grphw(MEM_CTL2, cfb->mem_ctl2, cfb);
1459 	}
1460 
1461 	/*
1462 	 * Ensure thatwe are using the correct PLL.
1463 	 * (CyberPro 5000's may be programmed to use
1464 	 * an additional set of PLLs.
1465 	 */
1466 	cyber2000fb_writeb(0xba, 0x3ce, cfb);
1467 	cyber2000fb_writeb(cyber2000fb_readb(0x3cf, cfb) & 0x80, 0x3cf, cfb);
1468 }
1469 
1470 static struct cfb_info * __devinit
cyberpro_alloc_fb_info(struct pci_dev * dev,const struct pci_device_id * id,char * name)1471 cyberpro_alloc_fb_info(struct pci_dev *dev, const struct pci_device_id *id, char *name)
1472 {
1473 	struct cfb_info *cfb;
1474 
1475 	cfb = kmalloc(sizeof(struct cfb_info) + sizeof(struct display) +
1476 		       sizeof(u32) * 16, GFP_KERNEL);
1477 
1478 	if (!cfb)
1479 		return NULL;
1480 
1481 	memset(cfb, 0, sizeof(struct cfb_info) + sizeof(struct display));
1482 
1483 	cfb->currcon		= -1;
1484 	cfb->dev		= dev;
1485 
1486 	if (id->driver_data == FB_ACCEL_IGS_CYBER5000)
1487 		cfb->ref_ps	= 40690; // 24.576 MHz
1488 	else
1489 		cfb->ref_ps	= 69842; // 14.31818 MHz (69841?)
1490 
1491 	cfb->divisors[0]	= 1;
1492 	cfb->divisors[1]	= 2;
1493 	cfb->divisors[2]	= 4;
1494 
1495 	if (id->driver_data == FB_ACCEL_IGS_CYBER2000)
1496 		cfb->divisors[3] = 8;
1497 	else
1498 		cfb->divisors[3] = 6;
1499 
1500 	strcpy(cfb->fb.fix.id, name);
1501 
1502 	cfb->fb.fix.type	= FB_TYPE_PACKED_PIXELS;
1503 	cfb->fb.fix.type_aux	= 0;
1504 	cfb->fb.fix.xpanstep	= 0;
1505 	cfb->fb.fix.ypanstep	= 1;
1506 	cfb->fb.fix.ywrapstep	= 0;
1507 	cfb->fb.fix.accel	= id->driver_data;
1508 
1509 	cfb->fb.var.nonstd	= 0;
1510 	cfb->fb.var.activate	= FB_ACTIVATE_NOW;
1511 	cfb->fb.var.height	= -1;
1512 	cfb->fb.var.width	= -1;
1513 	cfb->fb.var.accel_flags	= FB_ACCELF_TEXT;
1514 
1515 	strcpy(cfb->fb.modename, cfb->fb.fix.id);
1516 	strcpy(cfb->fb.fontname, default_font);
1517 
1518 	cfb->fb.fbops		= &cyber2000fb_ops;
1519 	cfb->fb.changevar	= NULL;
1520 	cfb->fb.switch_con	= cyber2000fb_switch;
1521 	cfb->fb.updatevar	= cyber2000fb_updatevar;
1522 	cfb->fb.blank		= cyber2000fb_blank;
1523 	cfb->fb.flags		= FBINFO_FLAG_DEFAULT;
1524 	cfb->fb.disp		= (struct display *)(cfb + 1);
1525 	cfb->fb.pseudo_palette	= (void *)(cfb->fb.disp + 1);
1526 
1527 	fb_alloc_cmap(&cfb->fb.cmap, NR_PALETTE, 0);
1528 
1529 	return cfb;
1530 }
1531 
1532 static void __devinit
cyberpro_free_fb_info(struct cfb_info * cfb)1533 cyberpro_free_fb_info(struct cfb_info *cfb)
1534 {
1535 	if (cfb) {
1536 		/*
1537 		 * Free the colourmap
1538 		 */
1539 		fb_alloc_cmap(&cfb->fb.cmap, 0, 0);
1540 
1541 		kfree(cfb);
1542 	}
1543 }
1544 
1545 /*
1546  * Parse Cyber2000fb options.  Usage:
1547  *  video=cyber2000:font:fontname
1548  */
1549 int
cyber2000fb_setup(char * options)1550 cyber2000fb_setup(char *options)
1551 {
1552 	char *opt;
1553 
1554 	if (!options || !*options)
1555 		return 0;
1556 
1557 	while ((opt = strsep(&options, ",")) != NULL) {
1558 		if (!*opt)
1559 			continue;
1560 
1561 		if (strncmp(opt, "font:", 5) == 0) {
1562 			strncpy(default_font_storage, opt + 5, sizeof(default_font_storage));
1563 			default_font = default_font_storage;
1564 			continue;
1565 		}
1566 
1567 		printk(KERN_ERR "CyberPro20x0: unknown parameter: %s\n", opt);
1568 	}
1569 	return 0;
1570 }
1571 
1572 static int __devinit
cyberpro_probe(struct pci_dev * dev,const struct pci_device_id * id)1573 cyberpro_probe(struct pci_dev *dev, const struct pci_device_id *id)
1574 {
1575 	struct cfb_info *cfb;
1576 	u_int h_sync, v_sync;
1577 	u_long smem_size;
1578 	char name[16];
1579 	int err;
1580 
1581 	sprintf(name, "CyberPro%4X", id->device);
1582 
1583 	err = pci_enable_device(dev);
1584 	if (err)
1585 		return err;
1586 
1587 	err = pci_request_regions(dev, name);
1588 	if (err)
1589 		return err;
1590 
1591 	err = -ENOMEM;
1592 	cfb = cyberpro_alloc_fb_info(dev, id, name);
1593 	if (!cfb)
1594 		goto failed_release;
1595 
1596 	cfb->region = ioremap(pci_resource_start(dev, 0),
1597 			      pci_resource_len(dev, 0));
1598 	if (!cfb->region)
1599 		goto failed_ioremap;
1600 
1601 	cfb->regs = cfb->region + MMIO_OFFSET;
1602 
1603 	cyberpro_init_hw(cfb, 1);
1604 
1605 	switch (cfb->mem_ctl2 & MEM_CTL2_SIZE_MASK) {
1606 	case MEM_CTL2_SIZE_4MB:	smem_size = 0x00400000; break;
1607 	case MEM_CTL2_SIZE_2MB:	smem_size = 0x00200000; break;
1608 	default:		smem_size = 0x00100000; break;
1609 	}
1610 
1611 	/*
1612 	 * Hmm, we _need_ a portable way of finding the address for
1613 	 * the remap stuff, both for mmio and for smem.
1614 	 */
1615 	cfb->fb.fix.mmio_start = pci_resource_start(dev, 0) + MMIO_OFFSET;
1616 	cfb->fb.fix.smem_start = pci_resource_start(dev, 0);
1617 	cfb->fb.fix.mmio_len   = MMIO_SIZE;
1618 	cfb->fb.fix.smem_len   = smem_size;
1619 	cfb->fb.screen_base    = cfb->region;
1620 
1621 	if (!fb_find_mode(&cfb->fb.var, &cfb->fb, NULL, NULL, 0,
1622 	    		  &cyber2000fb_default_mode, 8)) {
1623 		printk("%s: no valid mode found\n", cfb->fb.fix.id);
1624 		goto failed;
1625 	}
1626 
1627 	cfb->fb.var.yres_virtual = cfb->fb.fix.smem_len * 8 /
1628 			(cfb->fb.var.bits_per_pixel * cfb->fb.var.xres_virtual);
1629 
1630 	if (cfb->fb.var.yres_virtual < cfb->fb.var.yres)
1631 		cfb->fb.var.yres_virtual = cfb->fb.var.yres;
1632 
1633 	cyber2000fb_set_var(&cfb->fb.var, -1, &cfb->fb);
1634 
1635 	/*
1636 	 * Calculate the hsync and vsync frequencies.  Note that
1637 	 * we split the 1e12 constant up so that we can preserve
1638 	 * the precision and fit the results into 32-bit registers.
1639 	 *  (1953125000 * 512 = 1e12)
1640 	 */
1641 	h_sync = 1953125000 / cfb->fb.var.pixclock;
1642 	h_sync = h_sync * 512 / (cfb->fb.var.xres + cfb->fb.var.left_margin +
1643 		 cfb->fb.var.right_margin + cfb->fb.var.hsync_len);
1644 	v_sync = h_sync / (cfb->fb.var.yres + cfb->fb.var.upper_margin +
1645 		 cfb->fb.var.lower_margin + cfb->fb.var.vsync_len);
1646 
1647 	printk(KERN_INFO "%s: %dkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
1648 		cfb->fb.fix.id, cfb->fb.fix.smem_len >> 10,
1649 		cfb->fb.var.xres, cfb->fb.var.yres,
1650 		h_sync / 1000, h_sync % 1000, v_sync);
1651 
1652 	err = register_framebuffer(&cfb->fb);
1653 	if (err < 0)
1654 		goto failed;
1655 
1656 	/*
1657 	 * Our driver data
1658 	 */
1659 	pci_set_drvdata(dev, cfb);
1660 	if (int_cfb_info == NULL)
1661 		int_cfb_info = cfb;
1662 
1663 	return 0;
1664 
1665 failed:
1666 	iounmap(cfb->region);
1667 failed_ioremap:
1668 	cyberpro_free_fb_info(cfb);
1669 failed_release:
1670 	pci_release_regions(dev);
1671 
1672 	return err;
1673 }
1674 
cyberpro_remove(struct pci_dev * dev)1675 static void __devexit cyberpro_remove(struct pci_dev *dev)
1676 {
1677 	struct cfb_info *cfb = pci_get_drvdata(dev);
1678 
1679 	if (cfb) {
1680 		/*
1681 		 * If unregister_framebuffer fails, then
1682 		 * we will be leaving hooks that could cause
1683 		 * oopsen laying around.
1684 		 */
1685 		if (unregister_framebuffer(&cfb->fb))
1686 			printk(KERN_WARNING "%s: danger Will Robinson, "
1687 				"danger danger!  Oopsen imminent!\n",
1688 				cfb->fb.fix.id);
1689 		iounmap(cfb->region);
1690 		cyberpro_free_fb_info(cfb);
1691 
1692 		/*
1693 		 * Ensure that the driver data is no longer
1694 		 * valid.
1695 		 */
1696 		pci_set_drvdata(dev, NULL);
1697 		if (cfb == int_cfb_info)
1698 			int_cfb_info = NULL;
1699 
1700 		pci_release_regions(dev);
1701 	}
1702 }
1703 
cyberpro_suspend(struct pci_dev * dev,u32 state)1704 static int cyberpro_suspend(struct pci_dev *dev, u32 state)
1705 {
1706 	return 0;
1707 }
1708 
1709 /*
1710  * Re-initialise the CyberPro hardware
1711  */
cyberpro_resume(struct pci_dev * dev)1712 static int cyberpro_resume(struct pci_dev *dev)
1713 {
1714 	struct cfb_info *cfb = pci_get_drvdata(dev);
1715 
1716 	if (cfb) {
1717 		cyberpro_init_hw(cfb, 0);
1718 
1719 		/*
1720 		 * Restore the old video mode and the palette.
1721 		 * We also need to tell fbcon to redraw the console.
1722 		 */
1723 		cfb->fb.var.activate = FB_ACTIVATE_NOW;
1724 		cyber2000fb_set_var(&cfb->fb.var, -1, &cfb->fb);
1725 	}
1726 
1727 	return 0;
1728 }
1729 
1730 static struct pci_device_id cyberpro_pci_table[] __devinitdata = {
1731 	{ PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_2000,
1732 		PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_IGS_CYBER2000 },
1733 	{ PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_2010,
1734 		PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_IGS_CYBER2010 },
1735 	{ PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_5000,
1736 		PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_IGS_CYBER5000 },
1737 	{ 0, }
1738 };
1739 
1740 static struct pci_driver cyberpro_driver = {
1741 	name:		"CyberPro",
1742 	probe:		cyberpro_probe,
1743 	remove:		__devexit_p(cyberpro_remove),
1744 	suspend:	cyberpro_suspend,
1745 	resume:		cyberpro_resume,
1746 	id_table:	cyberpro_pci_table
1747 };
1748 
1749 /*
1750  * I don't think we can use the "module_init" stuff here because
1751  * the fbcon stuff may not be initialised yet.  Hence the #ifdef
1752  * around module_init.
1753  */
cyber2000fb_init(void)1754 int __init cyber2000fb_init(void)
1755 {
1756 	return pci_module_init(&cyberpro_driver);
1757 }
1758 
cyberpro_exit(void)1759 static void __exit cyberpro_exit(void)
1760 {
1761 	pci_unregister_driver(&cyberpro_driver);
1762 }
1763 
1764 #ifdef MODULE
1765 module_init(cyber2000fb_init);
1766 #endif
1767 module_exit(cyberpro_exit);
1768 
1769 MODULE_AUTHOR("Russell King");
1770 MODULE_DESCRIPTION("CyberPro 2000, 2010 and 5000 framebuffer driver");
1771 MODULE_DEVICE_TABLE(pci,cyberpro_pci_table);
1772 MODULE_LICENSE("GPL");
1773