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