1/*
2 * Frame buffer driver for Trident Cyberblade/i1 graphics core
3 *
4 * Copyright 2005 Knut Petersen <Knut_Petersen@t-online.de>
5 *
6 * CREDITS:
7 *	tridentfb.c by Jani Monoses
8 *	see files above for further credits
9 *
10 */
11
12#define CYBLAFB_DEBUG 0
13#define CYBLAFB_KD_GRAPHICS_QUIRK 1
14
15#define CYBLAFB_PIXMAPSIZE 8192
16
17#include <linux/module.h>
18#include <linux/string.h>
19#include <linux/fb.h>
20#include <linux/init.h>
21#include <linux/pci.h>
22#include <asm/types.h>
23#include <video/cyblafb.h>
24
25#define VERSION "0.62"
26
27struct cyblafb_par {
28	u32 pseudo_pal[16];
29	struct fb_ops ops;
30};
31
32static struct fb_fix_screeninfo cyblafb_fix __devinitdata = {
33	.id = "CyBla",
34	.type = FB_TYPE_PACKED_PIXELS,
35	.xpanstep = 1,
36	.ypanstep = 1,
37	.ywrapstep = 1,
38	.visual = FB_VISUAL_PSEUDOCOLOR,
39	.accel = FB_ACCEL_NONE,
40};
41
42static char *mode __devinitdata = NULL;
43static int bpp __devinitdata = 8;
44static int ref __devinitdata = 75;
45static int fp __devinitdata;
46static int crt __devinitdata;
47static int memsize __devinitdata;
48
49static int basestride;
50static int vesafb;
51static int nativex;
52static int center;
53static int stretch;
54static int pciwb = 1;
55static int pcirb = 1;
56static int pciwr = 1;
57static int pcirr = 1;
58static int disabled;
59static int verbosity;
60static int displaytype;
61
62static void __iomem *io_virt;	// iospace virtual memory address
63
64module_param(mode, charp, 0);
65module_param(bpp, int, 0);
66module_param(ref, int, 0);
67module_param(fp, int, 0);
68module_param(crt, int, 0);
69module_param(nativex, int, 0);
70module_param(center, int, 0);
71module_param(stretch, int, 0);
72module_param(pciwb, int, 0);
73module_param(pcirb, int, 0);
74module_param(pciwr, int, 0);
75module_param(pcirr, int, 0);
76module_param(memsize, int, 0);
77module_param(verbosity, int, 0);
78
79//=========================================
80//
81// Well, we have to fix the upper layers.
82// the bugs.
83//
84//=========================================
85
86#if (CYBLAFB_KD_GRAPHICS_QUIRK && CYBLAFB_DEBUG)
87	if (disabled) { \
88		printk("********\n");\
89		dump_stack();\
90		return val;\
91	}
92
93#elif CYBLAFB_KD_GRAPHICS_QUIRK
94#define KD_GRAPHICS_RETURN(val)\
95	if (disabled) {\
96		return val;\
97	}
98#else
99#define KD_GRAPHICS_RETURN(val)
100#endif
101
102//=========================================
103//
104// Port access macros for memory mapped io
105//
106//=========================================
107
108#define out8(r, v) writeb(v, io_virt + r)
109#define out32(r, v) writel(v, io_virt + r)
110#define in8(r) readb(io_virt + r)
111#define in32(r) readl(io_virt + r)
112
113//======================================
114//
115// Hardware access inline functions
116//
117//======================================
118
119static inline u8 read3X4(u32 reg)
120{
121	out8(0x3D4, reg);
122	return in8(0x3D5);
123}
124
125static inline u8 read3C4(u32 reg)
126{
127	out8(0x3C4, reg);
128	return in8(0x3C5);
129}
130
131static inline u8 read3CE(u32 reg)
132{
133	out8(0x3CE, reg);
134	return in8(0x3CF);
135}
136
137static inline void write3X4(u32 reg, u8 val)
138{
139	out8(0x3D4, reg);
140	out8(0x3D5, val);
141}
142
143static inline void write3C4(u32 reg, u8 val)
144{
145	out8(0x3C4, reg);
146	out8(0x3C5, val);
147}
148
149static inline void write3CE(u32 reg, u8 val)
150{
151	out8(0x3CE, reg);
152	out8(0x3CF, val);
153}
154
155static inline void write3C0(u32 reg, u8 val)
156{
157	in8(0x3DA);		// read to reset index
158	out8(0x3C0, reg);
159	out8(0x3C0, val);
160}
161
162//=================================================
163//
164// Enable memory mapped io and unprotect registers
165//
166//=================================================
167
168static void enable_mmio(void)
169{
170	u8 tmp;
171
172	outb(0x0B, 0x3C4);
173	inb(0x3C5);		// Set NEW mode
174	outb(SR0E, 0x3C4);	// write enable a lot of extended ports
175	outb(0x80, 0x3C5);
176
177	outb(SR11, 0x3C4);	// write enable those extended ports that
178	outb(0x87, 0x3C5);	// are not affected by SR0E_New
179
180	outb(CR1E, 0x3d4);	// clear write protect bit for port 0x3c2
181	tmp = inb(0x3d5) & 0xBF;
182	outb(CR1E, 0x3d4);
183	outb(tmp, 0x3d5);
184
185	outb(CR39, 0x3D4);
186	outb(inb(0x3D5) | 0x01, 0x3D5); // Enable mmio
187}
188
189//=================================================
190//
191// Set pixel clock VCLK1
192// - multipliers set elswhere
193// - freq in units of 0.01 MHz
194//
195// Hardware bug: SR18 >= 250 is broken for the
196//		 cyberblade/i1
197//
198//=================================================
199
200static void set_vclk(struct cyblafb_par *par, int freq)
201{
202	u32 m, n, k;
203	int f, fi, d, di;
204	u8 lo = 0, hi = 0;
205
206	d = 2000;
207	k = freq >= 10000 ? 0 : freq >= 5000 ? 1 : freq >= 2500 ? 2 : 3;
208	for (m = 0; m < 64; m++)
209		for (n = 0; n < 250; n++) {
210			fi = (int)(((5864727 * (n + 8)) /
211				    ((m + 2) * (1 << k))) >> 12);
212			if ((di = abs(fi - freq)) < d) {
213				d = di;
214				f = fi;
215				lo = (u8) n;
216				hi = (u8) ((k << 6) | m);
217			}
218		}
219	write3C4(SR19, hi);
220	write3C4(SR18, lo);
221	if (verbosity > 0)
222		output("pixclock = %d.%02d MHz, k/m/n %x %x %x\n",
223		       freq / 100, freq % 100, (hi & 0xc0) >> 6, hi & 0x3f, lo);
224}
225
226//================================================
227//
228// Cyberblade specific Graphics Engine (GE) setup
229//
230//================================================
231
232static void cyblafb_setup_GE(int pitch, int bpp)
233{
234	KD_GRAPHICS_RETURN();
235
236	switch (bpp) {
237	case 8:
238		basestride = ((pitch >> 3) << 20) | (0 << 29);
239		break;
240	case 15:
241		basestride = ((pitch >> 3) << 20) | (5 << 29);
242		break;
243	case 16:
244		basestride = ((pitch >> 3) << 20) | (1 << 29);
245		break;
246	case 24:
247	case 32:
248		basestride = ((pitch >> 3) << 20) | (2 << 29);
249		break;
250	}
251
252	write3X4(CR36, 0x90);	// reset GE
253	write3X4(CR36, 0x80);	// enable GE
254	out32(GE24, 1 << 7);	// reset all GE pointers by toggling
255	out32(GE24, 0); 	//   d7 of GE24
256	write3X4(CR2D, 0x00);	// GE Timinigs, no delays
257	out32(GE6C, 0); 	// Pattern and Style, p 129, ok
258}
259
260//=====================================================================
261//
262// Cyberblade specific syncing
263//
264//   A timeout might be caused by disabled mmio.
265//   Cause:
266//     - bit CR39 & 1 == 0 upon return, X trident driver bug
267//     - kdm bug (KD_GRAPHICS not set on first switch)
268//     - kernel design flaw (it believes in the correctness
269//	 of kdm/X
270//   First we try to sync ignoring that problem, as most of the
271//   time that will succeed immediately and the enable_mmio()
272//   would only degrade performance.
273//
274//=====================================================================
275
276static int cyblafb_sync(struct fb_info *info)
277{
278	u32 status, i = 100000;
279
280	KD_GRAPHICS_RETURN(0);
281
282	while (((status = in32(GE20)) & 0xFe800000) && i != 0)
283		i--;
284
285	if (i == 0) {
286		enable_mmio();
287		i = 1000000;
288		while (((status = in32(GE20)) & 0xFA800000) && i != 0)
289			i--;
290		if (i == 0) {
291			output("GE Timeout, status: %x\n", status);
292			if (status & 0x80000000)
293				output("Bresenham Engine : Busy\n");
294			if (status & 0x40000000)
295				output("Setup Engine     : Busy\n");
296			if (status & 0x20000000)
297				output("SP / DPE         : Busy\n");
298			if (status & 0x10000000)
299				output("Memory Interface : Busy\n");
300			if (status & 0x08000000)
301				output("Com Lst Proc     : Busy\n");
302			if (status & 0x04000000)
303				output("Block Write      : Busy\n");
304			if (status & 0x02000000)
305				output("Command Buffer   : Full\n");
306			if (status & 0x01000000)
307				output("RESERVED         : Busy\n");
308			if (status & 0x00800000)
309				output("PCI Write Buffer : Busy\n");
310			cyblafb_setup_GE(info->var.xres,
311					 info->var.bits_per_pixel);
312		}
313	}
314
315	return 0;
316}
317
318//==============================
319//
320// Cyberblade specific fillrect
321//
322//==============================
323
324static void cyblafb_fillrect(struct fb_info *info, const struct fb_fillrect *fr)
325{
326	u32 bpp = info->var.bits_per_pixel, col, desty, height;
327
328	KD_GRAPHICS_RETURN();
329
330	switch (bpp) {
331	default:
332	case 8:
333		col = fr->color;
334		col |= col << 8;
335		col |= col << 16;
336		break;
337	case 16:
338		col = ((u32 *) (info->pseudo_palette))[fr->color];
339		col |= col << 16;
340		break;
341	case 32:
342		col = ((u32 *) (info->pseudo_palette))[fr->color];
343		break;
344	}
345
346	desty = fr->dy;
347	height = fr->height;
348	while (height) {
349		out32(GEB8, basestride | ((desty * info->var.xres_virtual *
350					   bpp) >> 6));
351		out32(GE60, col);
352		out32(GE48, fr->rop ? 0x66 : ROP_S);
353		out32(GE44, 0x20000000 | 1 << 19 | 1 << 4 | 2 << 2);
354		out32(GE08, point(fr->dx, 0));
355		out32(GE0C, point(fr->dx + fr->width - 1,
356				  height > 4096 ? 4095 : height - 1));
357		if (likely(height <= 4096))
358			return;
359		desty += 4096;
360		height -= 4096;
361	}
362}
363
364//================================================
365//
366// Cyberblade specific copyarea
367//
368// This function silently assumes that it never
369// will be called with width or height exceeding
370// 4096.
371//
372//================================================
373
374static void cyblafb_copyarea(struct fb_info *info, const struct fb_copyarea *ca)
375{
376	u32 s1, s2, d1, d2, direction;
377
378	KD_GRAPHICS_RETURN();
379
380	s1 = point(ca->sx, 0);
381	s2 = point(ca->sx + ca->width - 1, ca->height - 1);
382	d1 = point(ca->dx, 0);
383	d2 = point(ca->dx + ca->width - 1, ca->height - 1);
384
385	if ((ca->sy > ca->dy) || ((ca->sy == ca->dy) && (ca->sx > ca->dx)))
386		direction = 0;
387	else
388		direction = 2;
389
390	out32(GEB8, basestride | ((ca->dy * info->var.xres_virtual *
391				   info->var.bits_per_pixel) >> 6));
392	out32(GEC8, basestride | ((ca->sy * info->var.xres_virtual *
393				   info->var.bits_per_pixel) >> 6));
394	out32(GE44, 0xa0000000 | 1 << 19 | 1 << 2 | direction);
395	out32(GE00, direction ? s2 : s1);
396	out32(GE04, direction ? s1 : s2);
397	out32(GE08, direction ? d2 : d1);
398	out32(GE0C, direction ? d1 : d2);
399}
400
401//=======================================================================
402//
403// Cyberblade specific imageblit
404//
405// Accelerated for the most usual case, blitting 1 - bit deep
406// character images. Everything else is passed to the generic imageblit
407// unless it is so insane that it is better to printk an alert.
408//
409// Hardware bug: _Never_ blit across pixel column 2048, that will lock
410// the system. We split those blit requests into three blitting
411// operations.
412//
413//=======================================================================
414
415static void cyblafb_imageblit(struct fb_info *info,
416			      const struct fb_image *image)
417{
418	u32 fgcol, bgcol;
419	u32 *pd = (u32 *) image->data;
420	u32 bpp = info->var.bits_per_pixel;
421
422	KD_GRAPHICS_RETURN();
423
424	// Used only for drawing the penguine (image->depth > 1)
425	if (image->depth != 1) {
426		cfb_imageblit(info, image);
427		return;
428	}
429	// That should never happen, but it would be fatal
430	if (image->width == 0 || image->height == 0) {
431		output("imageblit: width/height 0 detected\n");
432		return;
433	}
434
435	if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
436	    info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
437		fgcol = ((u32 *) (info->pseudo_palette))[image->fg_color];
438		bgcol = ((u32 *) (info->pseudo_palette))[image->bg_color];
439	} else {
440		fgcol = image->fg_color;
441		bgcol = image->bg_color;
442	}
443
444	switch (bpp) {
445	case 8:
446		fgcol |= fgcol << 8;
447		bgcol |= bgcol << 8;
448	case 16:
449		fgcol |= fgcol << 16;
450		bgcol |= bgcol << 16;
451	default:
452		break;
453	}
454
455	out32(GEB8, basestride | ((image->dy * info->var.xres_virtual *
456				   bpp) >> 6));
457	out32(GE60, fgcol);
458	out32(GE64, bgcol);
459
460	if (!(image->dx < 2048 && (image->dx + image->width - 1) >= 2048)) {
461		u32 dds = ((image->width + 31) >> 5) * image->height;
462		out32(GE44, 0xa0000000 | 1 << 20 | 1 << 19);
463		out32(GE08, point(image->dx, 0));
464		out32(GE0C, point(image->dx + image->width - 1,
465				  image->height - 1));
466		while (dds--)
467			out32(GE9C, *pd++);
468	} else {
469		int i, j;
470		u32 ddstotal = (image->width + 31) >> 5;
471		u32 ddsleft = (2048 - image->dx + 31) >> 5;
472		u32 skipleft = ddstotal - ddsleft;
473
474		out32(GE44, 0xa0000000 | 1 << 20 | 1 << 19);
475		out32(GE08, point(image->dx, 0));
476		out32(GE0C, point(2048 - 1, image->height - 1));
477		for (i = 0; i < image->height; i++) {
478			for (j = 0; j < ddsleft; j++)
479				out32(GE9C, *pd++);
480			pd += skipleft;
481		}
482
483		if (image->dx % 32) {
484			out32(GE44, 0xa0000000 | 1 << 20 | 1 << 19);
485			out32(GE08, point(2048, 0));
486			if (image->width > ddsleft << 5)
487				out32(GE0C, point(image->dx + (ddsleft << 5) -
488						  1, image->height - 1));
489			else
490				out32(GE0C, point(image->dx + image->width - 1,
491						  image->height - 1));
492			pd = ((u32 *) image->data) + ddstotal - skipleft - 1;
493			for (i = 0; i < image->height; i++) {
494				out32(GE9C, swab32(swab32(*pd) << ((32 -
495					    (image->dx & 31)) & 31)));
496				pd += ddstotal;
497			}
498		}
499
500		if (skipleft) {
501			out32(GE44, 0xa0000000 | 1 << 20 | 1 << 19);
502			out32(GE08, point(image->dx + (ddsleft << 5), 0));
503			out32(GE0C, point(image->dx + image->width - 1,
504					  image->height - 1));
505			pd = (u32 *) image->data;
506			for (i = 0; i < image->height; i++) {
507				pd += ddsleft;
508				for (j = 0; j < skipleft; j++)
509					out32(GE9C, *pd++);
510			}
511		}
512	}
513}
514
515//==========================================================
516//
517// Check if video mode is acceptable. We change var->??? if
518// video mode is slightly off or return error otherwise.
519// info->??? must not be changed!
520//
521//==========================================================
522
523static int cyblafb_check_var(struct fb_var_screeninfo *var,
524			     struct fb_info *info)
525{
526	int bpp = var->bits_per_pixel;
527
528	//
529	// we try to support 8, 16, 24 and 32 bpp modes,
530	// default to 8
531	//
532	// there is a 24 bpp mode, but for now we change requests to 32 bpp
533	// (This is what tridentfb does ... will be changed in the future)
534	//
535	//
536	if (bpp % 8 != 0 || bpp < 8 || bpp > 32)
537		bpp = 8;
538	if (bpp == 24)
539		bpp = var->bits_per_pixel = 32;
540
541	//
542	// interlaced modes are broken, fail if one is requested
543	//
544	if (var->vmode & FB_VMODE_INTERLACED)
545		return -EINVAL;
546
547	//
548	// fail if requested resolution is higher than physical
549	// flatpanel resolution
550	//
551	if ((displaytype == DISPLAY_FP) && nativex && var->xres > nativex)
552		return -EINVAL;
553
554	//
555	// we do not allow vclk to exceed 230 MHz. If the requested
556	// vclk is too high, we default to 200 MHz
557	//
558	if ((bpp == 32 ? 200000000 : 100000000) / var->pixclock > 23000)
559		var->pixclock = (bpp == 32 ? 200000000 : 100000000) / 20000;
560
561	//
562	// enforce (h|v)sync_len limits
563	//
564	var->hsync_len &= ~7;
565	if(var->hsync_len > 248)
566		var->hsync_len = 248;
567
568	var->vsync_len &= 15;
569
570	//
571	// Enforce horizontal and vertical hardware limits.
572	// 1600x1200 is mentioned as a maximum, but higher resolutions could
573	// work with slow refresh, small margins and short sync.
574	//
575	var->xres &= ~7;
576
577	if (((var->xres + var->left_margin + var->right_margin +
578			var->hsync_len) > (bpp == 32 ? 2040 : 4088)) ||
579			((var->yres + var->upper_margin + var->lower_margin +
580			var->vsync_len) > 2047))
581		return -EINVAL;
582
583	if ((var->xres > 1600) || (var->yres > 1200))
584		output("Mode %dx%d exceeds documented limits.\n",
585					   var->xres, var->yres);
586	//
587	// try to be smart about (x|y)res_virtual problems.
588	//
589	if (var->xres > var->xres_virtual)
590		var->xres_virtual = var->xres;
591	if (var->yres > var->yres_virtual)
592		var->yres_virtual = var->yres;
593
594	if (bpp == 8 || bpp == 16) {
595		if (var->xres_virtual > 4088)
596			var->xres_virtual = 4088;
597	} else {
598		if (var->xres_virtual > 2040)
599			var->xres_virtual = 2040;
600	}
601	var->xres_virtual &= ~7;
602	while (var->xres_virtual * var->yres_virtual * bpp / 8 >
603	       info->fix.smem_len) {
604		if (var->yres_virtual > var->yres)
605			var->yres_virtual--;
606		else if (var->xres_virtual > var->xres)
607			var->xres_virtual -= 8;
608		else
609			return -EINVAL;
610	}
611
612	switch (bpp) {
613	case 8:
614		var->red.offset = 0;
615		var->green.offset = 0;
616		var->blue.offset = 0;
617		var->red.length = 6;
618		var->green.length = 6;
619		var->blue.length = 6;
620		break;
621	case 16:
622		var->red.offset = 11;
623		var->green.offset = 5;
624		var->blue.offset = 0;
625		var->red.length = 5;
626		var->green.length = 6;
627		var->blue.length = 5;
628		break;
629	case 32:
630		var->red.offset = 16;
631		var->green.offset = 8;
632		var->blue.offset = 0;
633		var->red.length = 8;
634		var->green.length = 8;
635		var->blue.length = 8;
636		break;
637	default:
638		return -EINVAL;
639	}
640
641	return 0;
642}
643
644//=====================================================================
645//
646// Pan the display
647//
648// The datasheets defines crt start address to be 20 bits wide and
649// to be programmed to CR0C, CR0D, CR1E and CR27. Actually there is
650// CR2B[5] as an undocumented extension bit. Epia BIOS 2.07 does use
651// it, so it is also safe to be used here. BTW: datasheet CR0E on page
652// 90 really is CR1E, the real CRE is documented on page 72.
653//
654// BUT:
655//
656// As of internal version 0.60 we do not use vga panning any longer.
657// Vga panning did not allow us the use of all available video memory
658// and thus prevented ywrap scrolling. We do use the "right view"
659// register now.
660//
661//
662//=====================================================================
663
664static int cyblafb_pan_display(struct fb_var_screeninfo *var,
665			       struct fb_info *info)
666{
667	KD_GRAPHICS_RETURN(0);
668
669	info->var.xoffset = var->xoffset;
670	info->var.yoffset = var->yoffset;
671	out32(GE10, 0x80000000 | ((var->xoffset + (var->yoffset *
672		    var->xres_virtual)) * var->bits_per_pixel / 32));
673	return 0;
674}
675
676//============================================
677//
678// This will really help in case of a bug ...
679// dump most gaphics core registers.
680//
681//============================================
682
683static void regdump(struct cyblafb_par *par)
684{
685	int i;
686
687	if (verbosity < 2)
688		return;
689
690	printk("\n");
691	for (i = 0; i <= 0xff; i++) {
692		outb(i, 0x3d4);
693		printk("CR%02x=%02x ", i, inb(0x3d5));
694		if (i % 16 == 15)
695			printk("\n");
696	}
697
698	outb(0x30, 0x3ce);
699	outb(inb(0x3cf) | 0x40, 0x3cf);
700	for (i = 0; i <= 0x1f; i++) {
701		if (i == 0 || (i > 2 && i < 8) || i == 0x10 || i == 0x11
702		    || i == 0x16) {
703			outb(i, 0x3d4);
704			printk("CR%02x=%02x ", i, inb(0x3d5));
705		} else
706			printk("------- ");
707		if (i % 16 == 15)
708			printk("\n");
709	}
710	outb(0x30, 0x3ce);
711	outb(inb(0x3cf) & 0xbf, 0x3cf);
712
713	printk("\n");
714	for (i = 0; i <= 0x7f; i++) {
715		outb(i, 0x3ce);
716		printk("GR%02x=%02x ", i, inb(0x3cf));
717		if (i % 16 == 15)
718			printk("\n");
719	}
720
721	printk("\n");
722	for (i = 0; i <= 0xff; i++) {
723		outb(i, 0x3c4);
724		printk("SR%02x=%02x ", i, inb(0x3c5));
725		if (i % 16 == 15)
726			printk("\n");
727	}
728
729	printk("\n");
730	for (i = 0; i <= 0x1F; i++) {
731		inb(0x3da);	// next access is index!
732		outb(i, 0x3c0);
733		printk("AR%02x=%02x ", i, inb(0x3c1));
734		if (i % 16 == 15)
735			printk("\n");
736	}
737	printk("\n");
738
739	inb(0x3DA);		// reset internal flag to 3c0 index
740	outb(0x20, 0x3C0);	// enable attr
741
742	return;
743}
744
745//=======================================================================
746//
747// Save State
748//
749// This function is called while a switch to KD_TEXT is in progress,
750// before any of the other functions are called.
751//
752//=======================================================================
753
754static void cyblafb_save_state(struct fb_info *info)
755{
756	struct cyblafb_par *par = info->par;
757	if (verbosity > 0)
758		output("Switching to KD_TEXT\n");
759	disabled = 0;
760	regdump(par);
761	enable_mmio();
762	return;
763}
764
765//=======================================================================
766//
767// Restore State
768//
769// This function is called while a switch to KD_GRAPHICS is in progress,
770// We have to turn on vga style panning registers again because the
771// trident driver of X does not know about GE10.
772//
773//=======================================================================
774
775static void cyblafb_restore_state(struct fb_info *info)
776{
777	if (verbosity > 0)
778		output("Switching to KD_GRAPHICS\n");
779	out32(GE10, 0);
780	disabled = 1;
781	return;
782}
783
784//======================================
785//
786// Set hardware to requested video mode
787//
788//======================================
789
790static int cyblafb_set_par(struct fb_info *info)
791{
792	struct cyblafb_par *par = info->par;
793	u32 htotal, hdispend, hsyncstart, hsyncend, hblankstart,
794	    hblankend, preendfetch, vtotal, vdispend, vsyncstart,
795	    vsyncend, vblankstart, vblankend;
796	struct fb_var_screeninfo *var = &info->var;
797	int bpp = var->bits_per_pixel;
798	int i;
799
800	KD_GRAPHICS_RETURN(0);
801
802	if (verbosity > 0)
803		output("Switching to new mode: "
804		       "fbset -g %d %d %d %d %d -t %d %d %d %d %d %d %d\n",
805		       var->xres, var->yres, var->xres_virtual,
806		       var->yres_virtual, var->bits_per_pixel, var->pixclock,
807		       var->left_margin, var->right_margin, var->upper_margin,
808		       var->lower_margin, var->hsync_len, var->vsync_len);
809
810	htotal = (var->xres + var->left_margin + var->right_margin +
811		  var->hsync_len) / 8 - 5;
812	hdispend = var->xres / 8 - 1;
813	hsyncstart = (var->xres + var->right_margin) / 8;
814	hsyncend = var->hsync_len / 8;
815	hblankstart = hdispend + 1;
816	hblankend = htotal + 3; // should be htotal + 5, bios does it this way
817	preendfetch = ((var->xres >> 3) + 1) * ((bpp + 1) >> 3);
818
819	vtotal = var->yres + var->upper_margin + var->lower_margin +
820							var->vsync_len - 2;
821	vdispend = var->yres - 1;
822	vsyncstart = var->yres + var->lower_margin;
823	vblankstart = var->yres;
824	vblankend = vtotal; // should be vtotal + 2, but bios does it this way
825	vsyncend = var->vsync_len;
826
827	enable_mmio();		// necessary! ... check X ...
828
829	write3X4(CR11, read3X4(CR11) & 0x7F);	// unlock cr00 .. cr07
830
831	write3CE(GR30, 8);
832
833	if ((displaytype == DISPLAY_FP) && var->xres < nativex) {
834
835		// stretch or center ?
836
837		out8(0x3C2, 0xEB);
838
839		write3CE(GR30, read3CE(GR30) | 0x81);	// shadow mode on
840
841		if (center) {
842			write3CE(GR52, (read3CE(GR52) & 0x7C) | 0x80);
843			write3CE(GR53, (read3CE(GR53) & 0x7C) | 0x80);
844		} else if (stretch) {
845			write3CE(GR5D, 0);
846			write3CE(GR52, (read3CE(GR52) & 0x7C) | 1);
847			write3CE(GR53, (read3CE(GR53) & 0x7C) | 1);
848		}
849
850	} else {
851		out8(0x3C2, 0x2B);
852		write3CE(GR30, 8);
853	}
854
855	//
856	// Setup CRxx regs
857	//
858
859	write3X4(CR00, htotal & 0xFF);
860	write3X4(CR01, hdispend & 0xFF);
861	write3X4(CR02, hblankstart & 0xFF);
862	write3X4(CR03, hblankend & 0x1F);
863	write3X4(CR04, hsyncstart & 0xFF);
864	write3X4(CR05, (hsyncend & 0x1F) | ((hblankend & 0x20) << 2));
865	write3X4(CR06, vtotal & 0xFF);
866	write3X4(CR07, (vtotal & 0x100) >> 8 |
867		       (vdispend & 0x100) >> 7 |
868		       (vsyncstart & 0x100) >> 6 |
869		       (vblankstart & 0x100) >> 5 |
870		       0x10 |
871		       (vtotal & 0x200) >> 4 |
872		       (vdispend & 0x200) >> 3 | (vsyncstart & 0x200) >> 2);
873	write3X4(CR08, 0);
874	write3X4(CR09, (vblankstart & 0x200) >> 4 | 0x40 |	// FIX !!!
875		       ((info->var.vmode & FB_VMODE_DOUBLE) ? 0x80 : 0));
876	write3X4(CR0A, 0);	// Init to some reasonable default
877	write3X4(CR0B, 0);	// Init to some reasonable default
878	write3X4(CR0C, 0);	// Offset 0
879	write3X4(CR0D, 0);	// Offset 0
880	write3X4(CR0E, 0);	// Init to some reasonable default
881	write3X4(CR0F, 0);	// Init to some reasonable default
882	write3X4(CR10, vsyncstart & 0xFF);
883	write3X4(CR11, (vsyncend & 0x0F));
884	write3X4(CR12, vdispend & 0xFF);
885	write3X4(CR13, ((info->var.xres_virtual * bpp) / (4 * 16)) & 0xFF);
886	write3X4(CR14, 0x40);	// double word mode
887	write3X4(CR15, vblankstart & 0xFF);
888	write3X4(CR16, vblankend & 0xFF);
889	write3X4(CR17, 0xE3);
890	write3X4(CR18, 0xFF);
891	//	 CR19: needed for interlaced modes ... ignore it for now
892	write3X4(CR1A, 0x07);	// Arbitration Control Counter 1
893	write3X4(CR1B, 0x07);	// Arbitration Control Counter 2
894	write3X4(CR1C, 0x07);	// Arbitration Control Counter 3
895	write3X4(CR1D, 0x00);	// Don't know, doesn't hurt ; -)
896	write3X4(CR1E, (info->var.vmode & FB_VMODE_INTERLACED) ? 0x84 : 0x80);
897	//	 CR1F: do not set, contains BIOS info about memsize
898	write3X4(CR20, 0x20);	// enabe wr buf, disable 16bit planar mode
899	write3X4(CR21, 0x20);	// enable linear memory access
900	//	 CR22: RO cpu latch readback
901	//	 CR23: ???
902	//	 CR24: RO AR flag state
903	//	 CR25: RAMDAC rw timing, pclk buffer tristate control ????
904	//	 CR26: ???
905	write3X4(CR27, (vdispend & 0x400) >> 6 |
906		       (vsyncstart & 0x400) >> 5 |
907		       (vblankstart & 0x400) >> 4 |
908		       (vtotal & 0x400) >> 3 |
909		       0x8);
910	//	 CR28: ???
911	write3X4(CR29, (read3X4(CR29) & 0xCF) | ((((info->var.xres_virtual *
912			bpp) / (4 * 16)) & 0x300) >> 4));
913	write3X4(CR2A, read3X4(CR2A) | 0x40);
914	write3X4(CR2B, (htotal & 0x100) >> 8 |
915		       (hdispend & 0x100) >> 7 |
916		       // (0x00 & 0x100) >> 6 |   hinterlace para bit 8 ???
917		       (hsyncstart & 0x100) >> 5 |
918		       (hblankstart & 0x100) >> 4);
919	//	 CR2C: ???
920	//	 CR2D: initialized in cyblafb_setup_GE()
921	write3X4(CR2F, 0x92);	// conservative, better signal quality
922	//	 CR30: reserved
923	//	 CR31: reserved
924	//	 CR32: reserved
925	//	 CR33: reserved
926	//	 CR34: disabled in CR36
927	//	 CR35: disabled in CR36
928	//	 CR36: initialized in cyblafb_setup_GE
929	//	 CR37: i2c, ignore for now
930	write3X4(CR38, (bpp == 8) ? 0x00 :	//
931		       (bpp == 16) ? 0x05 :	// highcolor
932		       (bpp == 24) ? 0x29 :	// packed 24bit truecolor
933		       (bpp == 32) ? 0x09 : 0); // truecolor, 16 bit pixelbus
934	write3X4(CR39, 0x01 |	// MMIO enable
935		       (pcirb ? 0x02 : 0) |	// pci read burst enable
936		       (pciwb ? 0x04 : 0));	// pci write burst enable
937	write3X4(CR55, 0x1F | // pci clocks * 2 for STOP# during 1st data phase
938		       (pcirr ? 0x40 : 0) |	// pci read retry enable
939		       (pciwr ? 0x80 : 0));	// pci write retry enable
940	write3X4(CR56, preendfetch >> 8 < 2 ? (preendfetch >> 8 & 0x01) | 2
941					    : 0);
942	write3X4(CR57, preendfetch >> 8 < 2 ? preendfetch & 0xff : 0);
943	write3X4(CR58, 0x82);	// Bios does this .... don't know more
944	//
945	// Setup SRxx regs
946	//
947	write3C4(SR00, 3);
948	write3C4(SR01, 1);	//set char clock 8 dots wide
949	write3C4(SR02, 0x0F);	//enable 4 maps needed in chain4 mode
950	write3C4(SR03, 0);	//no character map select
951	write3C4(SR04, 0x0E);	//memory mode: ext mem, even, chain4
952
953	out8(0x3C4, 0x0b);
954	in8(0x3C5);		// Set NEW mode
955	write3C4(SR0D, 0x00);	// test ... check
956
957	set_vclk(par, (bpp == 32 ? 200000000 : 100000000)
958					/ info->var.pixclock);	//SR18, SR19
959
960	//
961	// Setup GRxx regs
962	//
963	write3CE(GR00, 0x00);	// test ... check
964	write3CE(GR01, 0x00);	// test ... check
965	write3CE(GR02, 0x00);	// test ... check
966	write3CE(GR03, 0x00);	// test ... check
967	write3CE(GR04, 0x00);	// test ... check
968	write3CE(GR05, 0x40);	// no CGA compat, allow 256 col
969	write3CE(GR06, 0x05);	// graphics mode
970	write3CE(GR07, 0x0F);	// planes?
971	write3CE(GR08, 0xFF);	// test ... check
972	write3CE(GR0F, (bpp == 32) ? 0x1A : 0x12); // vclk / 2 if 32bpp, chain4
973	write3CE(GR20, 0xC0);	// test ... check
974	write3CE(GR2F, 0xA0);	// PCLK = VCLK, no skew,
975
976	//
977	// Setup ARxx regs
978	//
979	for (i = 0; i < 0x10; i++)	// set AR00 .. AR0f
980		write3C0(i, i);
981	write3C0(AR10, 0x41);	// graphics mode and support 256 color modes
982	write3C0(AR12, 0x0F);	// planes
983	write3C0(AR13, 0);	// horizontal pel panning
984	in8(0x3DA);		// reset internal flag to 3c0 index
985	out8(0x3C0, 0x20);	// enable attr
986
987	//
988	// Setup hidden RAMDAC command register
989	//
990	in8(0x3C8);		// these reads are
991	in8(0x3C6);		// necessary to
992	in8(0x3C6);		// unmask the RAMDAC
993	in8(0x3C6);		// command reg, otherwise
994	in8(0x3C6);		// we would write the pixelmask reg!
995	out8(0x3C6, (bpp == 8) ? 0x00 : // 256 colors
996	     (bpp == 15) ? 0x10 :	//
997	     (bpp == 16) ? 0x30 :	// hicolor
998	     (bpp == 24) ? 0xD0 :	// truecolor
999	     (bpp == 32) ? 0xD0 : 0);	// truecolor
1000	in8(0x3C8);
1001
1002	//
1003	// GR31 is not mentioned in the datasheet
1004	//
1005	if (displaytype == DISPLAY_FP)
1006		write3CE(GR31, (read3CE(GR31) & 0x8F) |
1007			 ((info->var.yres > 1024) ? 0x50 :
1008			  (info->var.yres > 768) ? 0x30 :
1009			  (info->var.yres > 600) ? 0x20 :
1010			  (info->var.yres > 480) ? 0x10 : 0));
1011
1012	info->fix.visual = (bpp == 8) ? FB_VISUAL_PSEUDOCOLOR
1013				      : FB_VISUAL_TRUECOLOR;
1014	info->fix.line_length = info->var.xres_virtual * (bpp >> 3);
1015	info->cmap.len = (bpp == 8) ? 256 : 16;
1016
1017	//
1018	// init acceleration engine
1019	//
1020	cyblafb_setup_GE(info->var.xres_virtual, info->var.bits_per_pixel);
1021
1022	//
1023	// Set/clear flags to allow proper scroll mode selection.
1024	//
1025	if (var->xres == var->xres_virtual)
1026		info->flags &= ~FBINFO_HWACCEL_XPAN;
1027	else
1028		info->flags |= FBINFO_HWACCEL_XPAN;
1029
1030	if (var->yres == var->yres_virtual)
1031		info->flags &= ~FBINFO_HWACCEL_YPAN;
1032	else
1033		info->flags |= FBINFO_HWACCEL_YPAN;
1034
1035	if (info->fix.smem_len !=
1036	    var->xres_virtual * var->yres_virtual * bpp / 8)
1037		info->flags &= ~FBINFO_HWACCEL_YWRAP;
1038	else
1039		info->flags |= FBINFO_HWACCEL_YWRAP;
1040
1041	regdump(par);
1042
1043	return 0;
1044}
1045
1046//========================
1047//
1048// Set one color register
1049//
1050//========================
1051
1052static int cyblafb_setcolreg(unsigned regno, unsigned red, unsigned green,
1053			     unsigned blue, unsigned transp,
1054			     struct fb_info *info)
1055{
1056	int bpp = info->var.bits_per_pixel;
1057
1058	KD_GRAPHICS_RETURN(0);
1059
1060	if (regno >= info->cmap.len)
1061		return 1;
1062
1063	if (bpp == 8) {
1064		out8(0x3C6, 0xFF);
1065		out8(0x3C8, regno);
1066		out8(0x3C9, red >> 10);
1067		out8(0x3C9, green >> 10);
1068		out8(0x3C9, blue >> 10);
1069
1070	} else if (bpp == 16)	// RGB 565
1071		((u32 *) info->pseudo_palette)[regno] =
1072		    (red & 0xF800) |
1073		    ((green & 0xFC00) >> 5) | ((blue & 0xF800) >> 11);
1074	else if (bpp == 32)	// ARGB 8888
1075		((u32 *) info->pseudo_palette)[regno] =
1076		    ((transp & 0xFF00) << 16) |
1077		    ((red & 0xFF00) << 8) |
1078		    ((green & 0xFF00)) | ((blue & 0xFF00) >> 8);
1079
1080	return 0;
1081}
1082
1083//==========================================================
1084//
1085// Try blanking the screen. For flat panels it does nothing
1086//
1087//==========================================================
1088
1089static int cyblafb_blank(int blank_mode, struct fb_info *info)
1090{
1091	unsigned char PMCont, DPMSCont;
1092
1093	KD_GRAPHICS_RETURN(0);
1094
1095	if (displaytype == DISPLAY_FP)
1096		return 0;
1097
1098	out8(0x83C8, 0x04);	// DPMS Control
1099	PMCont = in8(0x83C6) & 0xFC;
1100
1101	DPMSCont = read3CE(GR23) & 0xFC;
1102
1103	switch (blank_mode) {
1104	case FB_BLANK_UNBLANK:	// Screen: On, HSync: On, VSync: On
1105	case FB_BLANK_NORMAL:	// Screen: Off, HSync: On, VSync: On
1106		PMCont |= 0x03;
1107		DPMSCont |= 0x00;
1108		break;
1109	case FB_BLANK_HSYNC_SUSPEND:	// Screen: Off, HSync: Off, VSync: On
1110		PMCont |= 0x02;
1111		DPMSCont |= 0x01;
1112		break;
1113	case FB_BLANK_VSYNC_SUSPEND:	// Screen: Off, HSync: On, VSync: Off
1114		PMCont |= 0x02;
1115		DPMSCont |= 0x02;
1116		break;
1117	case FB_BLANK_POWERDOWN:	// Screen: Off, HSync: Off, VSync: Off
1118		PMCont |= 0x00;
1119		DPMSCont |= 0x03;
1120		break;
1121	}
1122
1123	write3CE(GR23, DPMSCont);
1124	out8(0x83C8, 4);
1125	out8(0x83C6, PMCont);
1126	//
1127	// let fbcon do a softblank for us
1128	//
1129	return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1130}
1131
1132static struct fb_ops cyblafb_ops __devinitdata = {
1133	.owner = THIS_MODULE,
1134	.fb_setcolreg = cyblafb_setcolreg,
1135	.fb_pan_display = cyblafb_pan_display,
1136	.fb_blank = cyblafb_blank,
1137	.fb_check_var = cyblafb_check_var,
1138	.fb_set_par = cyblafb_set_par,
1139	.fb_fillrect = cyblafb_fillrect,
1140	.fb_copyarea = cyblafb_copyarea,
1141	.fb_imageblit = cyblafb_imageblit,
1142	.fb_sync = cyblafb_sync,
1143	.fb_restore_state = cyblafb_restore_state,
1144	.fb_save_state = cyblafb_save_state,
1145};
1146
1147//==========================================================================
1148//
1149// getstartupmode() decides about the inital video mode
1150//
1151// There is no reason to use modedb, a lot of video modes there would
1152// need altered timings to display correctly. So I decided that it is much
1153// better to provide a limited optimized set of modes plus the option of
1154// using the mode in effect at startup time (might be selected using the
1155// vga=??? paramter). After that the user might use fbset to select any
1156// mode he likes, check_var will not try to alter geometry parameters as
1157// it would be necessary otherwise.
1158//
1159//==========================================================================
1160
1161static int __devinit getstartupmode(struct fb_info *info)
1162{
1163	u32 htotal, hdispend, hsyncstart, hsyncend, hblankstart, hblankend,
1164	    vtotal, vdispend, vsyncstart, vsyncend, vblankstart, vblankend,
1165	    cr00, cr01, cr02, cr03, cr04, cr05, cr2b,
1166	    cr06, cr07, cr09, cr10, cr11, cr12, cr15, cr16, cr27,
1167	    cr38, sr0d, sr18, sr19, gr0f, fi, pxclkdiv, vclkdiv, tmp, i;
1168
1169	struct modus {
1170		int xres; int vxres; int yres; int vyres;
1171		int bpp; int pxclk;
1172		int left_margin; int right_margin;
1173		int upper_margin; int lower_margin;
1174		int hsync_len; int vsync_len;
1175	} modedb[5] = {
1176		{
1177		0, 2048, 0, 4096, 0, 0, 0, 0, 0, 0, 0, 0}, {
1178		640, 2048, 480, 4096, 0, 0, -40, 24, 17, 0, 216, 3}, {
1179		800, 2048, 600, 4096, 0, 0, 96, 24, 14, 0, 136, 11}, {
1180		1024, 2048, 768, 4096, 0, 0, 144, 24, 29, 0, 120, 3}, {
1181		1280, 2048, 1024, 4096, 0, 0, 232, 16, 39, 0, 160, 3}
1182	};
1183
1184	outb(0x00, 0x3d4); cr00 = inb(0x3d5);
1185	outb(0x01, 0x3d4); cr01 = inb(0x3d5);
1186	outb(0x02, 0x3d4); cr02 = inb(0x3d5);
1187	outb(0x03, 0x3d4); cr03 = inb(0x3d5);
1188	outb(0x04, 0x3d4); cr04 = inb(0x3d5);
1189	outb(0x05, 0x3d4); cr05 = inb(0x3d5);
1190	outb(0x06, 0x3d4); cr06 = inb(0x3d5);
1191	outb(0x07, 0x3d4); cr07 = inb(0x3d5);
1192	outb(0x09, 0x3d4); cr09 = inb(0x3d5);
1193	outb(0x10, 0x3d4); cr10 = inb(0x3d5);
1194	outb(0x11, 0x3d4); cr11 = inb(0x3d5);
1195	outb(0x12, 0x3d4); cr12 = inb(0x3d5);
1196	outb(0x15, 0x3d4); cr15 = inb(0x3d5);
1197	outb(0x16, 0x3d4); cr16 = inb(0x3d5);
1198	outb(0x27, 0x3d4); cr27 = inb(0x3d5);
1199	outb(0x2b, 0x3d4); cr2b = inb(0x3d5);
1200	outb(0x38, 0x3d4); cr38 = inb(0x3d5);
1201
1202	outb(0x0b, 0x3c4);
1203	inb(0x3c5);
1204
1205	outb(0x0d, 0x3c4); sr0d = inb(0x3c5);
1206	outb(0x18, 0x3c4); sr18 = inb(0x3c5);
1207	outb(0x19, 0x3c4); sr19 = inb(0x3c5);
1208	outb(0x0f, 0x3ce); gr0f = inb(0x3cf);
1209
1210	htotal = cr00 | (cr2b & 0x01) << 8;
1211	hdispend = cr01 | (cr2b & 0x02) << 7;
1212	hblankstart = cr02 | (cr2b & 0x10) << 4;
1213	hblankend = (cr03 & 0x1f) | (cr05 & 0x80) >> 2;
1214	hsyncstart = cr04 | (cr2b & 0x08) << 5;
1215	hsyncend = cr05 & 0x1f;
1216
1217	modedb[0].xres = hblankstart * 8;
1218	modedb[0].hsync_len = hsyncend * 8;
1219	modedb[0].right_margin = hsyncstart * 8 - modedb[0].xres;
1220	modedb[0].left_margin = (htotal + 5) * 8 - modedb[0].xres -
1221	    modedb[0].right_margin - modedb[0].hsync_len;
1222
1223	vtotal = cr06 | (cr07 & 0x01) << 8 | (cr07 & 0x20) << 4
1224	    | (cr27 & 0x80) << 3;
1225	vdispend = cr12 | (cr07 & 0x02) << 7 | (cr07 & 0x40) << 3
1226	    | (cr27 & 0x10) << 6;
1227	vsyncstart = cr10 | (cr07 & 0x04) << 6 | (cr07 & 0x80) << 2
1228	    | (cr27 & 0x20) << 5;
1229	vsyncend = cr11 & 0x0f;
1230	vblankstart = cr15 | (cr07 & 0x08) << 5 | (cr09 & 0x20) << 4
1231	    | (cr27 & 0x40) << 4;
1232	vblankend = cr16;
1233
1234	modedb[0].yres = vdispend + 1;
1235	modedb[0].vsync_len = vsyncend;
1236	modedb[0].lower_margin = vsyncstart - modedb[0].yres;
1237	modedb[0].upper_margin = vtotal - modedb[0].yres -
1238	    modedb[0].lower_margin - modedb[0].vsync_len + 2;
1239
1240	tmp = cr38 & 0x3c;
1241	modedb[0].bpp = tmp == 0 ? 8 : tmp == 4 ? 16 : tmp == 28 ? 24 :
1242	    tmp == 8 ? 32 : 8;
1243
1244	fi = ((5864727 * (sr18 + 8)) /
1245	      (((sr19 & 0x3f) + 2) * (1 << ((sr19 & 0xc0) >> 6)))) >> 12;
1246	pxclkdiv = ((gr0f & 0x08) >> 3 | (gr0f & 0x40) >> 5) + 1;
1247	tmp = sr0d & 0x06;
1248	vclkdiv = tmp == 0 ? 2 : tmp == 2 ? 4 : tmp == 4 ? 8 : 3; // * 2 !
1249	modedb[0].pxclk = ((100000000 * pxclkdiv * vclkdiv) >> 1) / fi;
1250
1251	if (verbosity > 0)
1252		output("detected startup mode: "
1253		       "fbset -g %d %d %d ??? %d -t %d %d %d %d %d %d %d\n",
1254		       modedb[0].xres, modedb[0].yres, modedb[0].xres,
1255		       modedb[0].bpp, modedb[0].pxclk, modedb[0].left_margin,
1256		       modedb[0].right_margin, modedb[0].upper_margin,
1257		       modedb[0].lower_margin, modedb[0].hsync_len,
1258		       modedb[0].vsync_len);
1259
1260	//
1261	// We use this goto target in case of a failed check_var. No, I really
1262	// do not want to do it in another way!
1263	//
1264
1265      tryagain:
1266
1267	i = (mode == NULL) ? 0 :
1268	    !strncmp(mode, "640x480", 7) ? 1 :
1269	    !strncmp(mode, "800x600", 7) ? 2 :
1270	    !strncmp(mode, "1024x768", 8) ? 3 :
1271	    !strncmp(mode, "1280x1024", 9) ? 4 : 0;
1272
1273	ref = (ref < 50) ? 50 : (ref > 85) ? 85 : ref;
1274
1275	if (i == 0) {
1276		info->var.pixclock = modedb[i].pxclk;
1277		info->var.bits_per_pixel = modedb[i].bpp;
1278	} else {
1279		info->var.pixclock = (100000000 /
1280				      ((modedb[i].left_margin +
1281					modedb[i].xres +
1282					modedb[i].right_margin +
1283					modedb[i].hsync_len) *
1284				       (modedb[i].upper_margin +
1285					modedb[i].yres +
1286					modedb[i].lower_margin +
1287					modedb[i].vsync_len) * ref / 10000));
1288		info->var.bits_per_pixel = bpp;
1289	}
1290
1291	info->var.left_margin = modedb[i].left_margin;
1292	info->var.right_margin = modedb[i].right_margin;
1293	info->var.xres = modedb[i].xres;
1294	if (!(modedb[i].yres == 1280 && modedb[i].bpp == 32))
1295		info->var.xres_virtual = modedb[i].vxres;
1296	else
1297		info->var.xres_virtual = modedb[i].xres;
1298	info->var.xoffset = 0;
1299	info->var.hsync_len = modedb[i].hsync_len;
1300	info->var.upper_margin = modedb[i].upper_margin;
1301	info->var.yres = modedb[i].yres;
1302	info->var.yres_virtual = modedb[i].vyres;
1303	info->var.yoffset = 0;
1304	info->var.lower_margin = modedb[i].lower_margin;
1305	info->var.vsync_len = modedb[i].vsync_len;
1306	info->var.sync = 0;
1307	info->var.vmode = FB_VMODE_NONINTERLACED;
1308
1309	if (cyblafb_check_var(&info->var, info)) {
1310		// 640x480 - 8@75 should really never fail. One case would
1311		// be fp == 1 and nativex < 640 ... give up then
1312		if (i == 1 && bpp == 8 && ref == 75) {
1313			output("Can't find a valid mode :-(\n");
1314			return -EINVAL;
1315		}
1316		// Our detected mode is unlikely to fail. If it does,
1317		// try 640x480 - 8@75 ...
1318		if (i == 0) {
1319			mode = "640x480";
1320			bpp = 8;
1321			ref = 75;
1322			output("Detected mode failed check_var! "
1323			       "Trying 640x480 - 8@75\n");
1324			goto tryagain;
1325		}
1326		// A specified video mode failed for some reason.
1327		// Try the startup mode first
1328		output("Specified mode '%s' failed check! "
1329		       "Falling back to startup mode.\n", mode);
1330		mode = NULL;
1331		goto tryagain;
1332	}
1333
1334	return 0;
1335}
1336
1337//========================================================
1338//
1339// Detect activated memory size. Undefined values require
1340// memsize parameter.
1341//
1342//========================================================
1343
1344static unsigned int __devinit get_memsize(void)
1345{
1346	unsigned char tmp;
1347	unsigned int k;
1348
1349	if (memsize)
1350		k = memsize * Kb;
1351	else {
1352		tmp = read3X4(CR1F) & 0x0F;
1353		switch (tmp) {
1354		case 0x03:
1355			k = 1 * 1024 * 1024;
1356			break;
1357		case 0x07:
1358			k = 2 * 1024 * 1024;
1359			break;
1360		case 0x0F:
1361			k = 4 * 1024 * 1024;
1362			break;
1363		case 0x04:
1364			k = 8 * 1024 * 1024;
1365			break;
1366		default:
1367			k = 1 * 1024 * 1024;
1368			output("Unknown memory size code %x in CR1F."
1369			       " We default to 1 Mb for now, please"
1370			       " do provide a memsize parameter!\n", tmp);
1371		}
1372	}
1373
1374	if (verbosity > 0)
1375		output("framebuffer size = %d Kb\n", k / Kb);
1376	return k;
1377}
1378
1379//=========================================================
1380//
1381// Detect if a flat panel monitor connected to the special
1382// interface is active. Override is possible by fp and crt
1383// parameters.
1384//
1385//=========================================================
1386
1387static unsigned int __devinit get_displaytype(void)
1388{
1389	if (fp)
1390		return DISPLAY_FP;
1391	if (crt)
1392		return DISPLAY_CRT;
1393	return (read3CE(GR33) & 0x10) ? DISPLAY_FP : DISPLAY_CRT;
1394}
1395
1396//=====================================
1397//
1398// Get native resolution of flat panel
1399//
1400//=====================================
1401
1402static int __devinit get_nativex(void)
1403{
1404	int x, y, tmp;
1405
1406	if (nativex)
1407		return nativex;
1408
1409	tmp = (read3CE(GR52) >> 4) & 3;
1410
1411	switch (tmp) {
1412	case 0: x = 1280; y = 1024;
1413		break;
1414	case 2: x = 1024; y = 768;
1415		break;
1416	case 3: x = 800;  y = 600;
1417		break;
1418	case 4: x = 1400; y = 1050;
1419		break;
1420	case 1:
1421	default:
1422		x = 640; y = 480;
1423		break;
1424	}
1425
1426	if (verbosity > 0)
1427		output("%dx%d flat panel found\n", x, y);
1428	return x;
1429}
1430
1431static int __devinit cybla_pci_probe(struct pci_dev *dev,
1432				     const struct pci_device_id *id)
1433{
1434	struct fb_info *info;
1435	struct cyblafb_par *par;
1436
1437	info = framebuffer_alloc(sizeof(struct cyblafb_par), &dev->dev);
1438	if (!info)
1439		goto errout_alloc_info;
1440
1441	info->pixmap.addr = kzalloc(CYBLAFB_PIXMAPSIZE, GFP_KERNEL);
1442	if (!info->pixmap.addr) {
1443		output("allocation of pixmap buffer failed!\n");
1444		goto errout_alloc_pixmap;
1445	}
1446	info->pixmap.size = CYBLAFB_PIXMAPSIZE - 4;
1447	info->pixmap.buf_align = 4;
1448	info->pixmap.access_align = 32;
1449	info->pixmap.flags = FB_PIXMAP_SYSTEM;
1450	info->pixmap.scan_align = 4;
1451
1452	par = info->par;
1453	par->ops = cyblafb_ops;
1454
1455	info->fix = cyblafb_fix;
1456	info->fbops = &par->ops;
1457	info->fix = cyblafb_fix;
1458
1459	if (pci_enable_device(dev)) {
1460		output("could not enable device!\n");
1461		goto errout_enable;
1462	}
1463	// might already be requested by vga console or vesafb,
1464	// so we do care about success
1465	if (!request_region(0x3c0, 0x20, "cyblafb")) {
1466		output("region 0x3c0/0x20 already reserved\n");
1467		vesafb |= 1;
1468
1469	}
1470	//
1471	// Graphics Engine Registers
1472	//
1473	if (!request_region(GEBase, 0x100, "cyblafb")) {
1474		output("region %#x/0x100 already reserved\n", GEBase);
1475		vesafb |= 2;
1476	}
1477
1478	regdump(par);
1479
1480	enable_mmio();
1481
1482	// setup MMIO region
1483	info->fix.mmio_start = pci_resource_start(dev, 1);
1484	info->fix.mmio_len = 0x20000;
1485
1486	if (!request_mem_region(info->fix.mmio_start,
1487				info->fix.mmio_len, "cyblafb")) {
1488		output("request_mem_region failed for mmio region!\n");
1489		goto errout_mmio_reqmem;
1490	}
1491
1492	io_virt = ioremap_nocache(info->fix.mmio_start, info->fix.mmio_len);
1493
1494	if (!io_virt) {
1495		output("ioremap failed for mmio region\n");
1496		goto errout_mmio_remap;
1497	}
1498	// setup framebuffer memory ... might already be requested
1499	// by vesafb. Not to fail in case of an unsuccessful request
1500	// is useful if both are loaded.
1501	info->fix.smem_start = pci_resource_start(dev, 0);
1502	info->fix.smem_len = get_memsize();
1503
1504	if (!request_mem_region(info->fix.smem_start,
1505				info->fix.smem_len, "cyblafb")) {
1506		output("region %#lx/%#x already reserved\n",
1507		       info->fix.smem_start, info->fix.smem_len);
1508		vesafb |= 4;
1509	}
1510
1511	info->screen_base = ioremap_nocache(info->fix.smem_start,
1512					    info->fix.smem_len);
1513
1514	if (!info->screen_base) {
1515		output("ioremap failed for smem region\n");
1516		goto errout_smem_remap;
1517	}
1518
1519	displaytype = get_displaytype();
1520
1521	if (displaytype == DISPLAY_FP)
1522		nativex = get_nativex();
1523
1524	info->flags = FBINFO_DEFAULT
1525		    | FBINFO_HWACCEL_COPYAREA
1526		    | FBINFO_HWACCEL_FILLRECT
1527		    | FBINFO_HWACCEL_IMAGEBLIT
1528		    | FBINFO_READS_FAST
1529//		    | FBINFO_PARTIAL_PAN_OK
1530		    | FBINFO_MISC_ALWAYS_SETPAR;
1531
1532	info->pseudo_palette = par->pseudo_pal;
1533
1534	if (getstartupmode(info))
1535		goto errout_findmode;
1536
1537	fb_alloc_cmap(&info->cmap, 256, 0);
1538
1539	if (register_framebuffer(info)) {
1540		output("Could not register CyBla framebuffer\n");
1541		goto errout_register;
1542	}
1543
1544	pci_set_drvdata(dev, info);
1545
1546	//
1547	// normal exit and error paths
1548	//
1549
1550	return 0;
1551
1552      errout_register:
1553      errout_findmode:
1554	iounmap(info->screen_base);
1555      errout_smem_remap:
1556	if (!(vesafb & 4))
1557		release_mem_region(info->fix.smem_start, info->fix.smem_len);
1558	iounmap(io_virt);
1559      errout_mmio_remap:
1560	release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
1561      errout_mmio_reqmem:
1562	if (!(vesafb & 1))
1563		release_region(0x3c0, 32);
1564      errout_enable:
1565	kfree(info->pixmap.addr);
1566      errout_alloc_pixmap:
1567	framebuffer_release(info);
1568      errout_alloc_info:
1569	output("CyblaFB version %s aborting init.\n", VERSION);
1570	return -ENODEV;
1571}
1572
1573static void __devexit cybla_pci_remove(struct pci_dev *dev)
1574{
1575	struct fb_info *info = pci_get_drvdata(dev);
1576
1577	unregister_framebuffer(info);
1578	iounmap(io_virt);
1579	iounmap(info->screen_base);
1580	if (!(vesafb & 4))
1581		release_mem_region(info->fix.smem_start, info->fix.smem_len);
1582	release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
1583	fb_dealloc_cmap(&info->cmap);
1584	if (!(vesafb & 2))
1585		release_region(GEBase, 0x100);
1586	if (!(vesafb & 1))
1587		release_region(0x3c0, 32);
1588	kfree(info->pixmap.addr);
1589	framebuffer_release(info);
1590	output("CyblaFB version %s normal exit.\n", VERSION);
1591}
1592
1593//
1594// List of boards that we are trying to support
1595//
1596static struct pci_device_id cybla_devices[] = {
1597	{PCI_VENDOR_ID_TRIDENT, CYBERBLADEi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
1598	{0,}
1599};
1600
1601MODULE_DEVICE_TABLE(pci, cybla_devices);
1602
1603static struct pci_driver cyblafb_pci_driver = {
1604	.name = "cyblafb",
1605	.id_table = cybla_devices,
1606	.probe = cybla_pci_probe,
1607	.remove = __devexit_p(cybla_pci_remove)
1608};
1609
1610//=============================================================
1611//
1612// kernel command line example:
1613//
1614//	video=cyblafb:1280x1024, bpp=16, ref=50 ...
1615//
1616// modprobe command line example:
1617//
1618//	modprobe cyblafb mode=1280x1024 bpp=16 ref=50 ...
1619//
1620//=============================================================
1621
1622static int __devinit cyblafb_init(void)
1623{
1624#ifndef MODULE
1625	char *options = NULL;
1626	char *opt;
1627
1628	if (fb_get_options("cyblafb", &options))
1629		return -ENODEV;
1630
1631	if (options && *options)
1632		while ((opt = strsep(&options, ",")) != NULL) {
1633			if (!*opt)
1634				continue;
1635			else if (!strncmp(opt, "bpp=", 4))
1636				bpp = simple_strtoul(opt + 4, NULL, 0);
1637			else if (!strncmp(opt, "ref=", 4))
1638				ref = simple_strtoul(opt + 4, NULL, 0);
1639			else if (!strncmp(opt, "fp", 2))
1640				displaytype = DISPLAY_FP;
1641			else if (!strncmp(opt, "crt", 3))
1642				displaytype = DISPLAY_CRT;
1643			else if (!strncmp(opt, "nativex=", 8))
1644				nativex = simple_strtoul(opt + 8, NULL, 0);
1645			else if (!strncmp(opt, "center", 6))
1646				center = 1;
1647			else if (!strncmp(opt, "stretch", 7))
1648				stretch = 1;
1649			else if (!strncmp(opt, "pciwb=", 6))
1650				pciwb = simple_strtoul(opt + 6, NULL, 0);
1651			else if (!strncmp(opt, "pcirb=", 6))
1652				pcirb = simple_strtoul(opt + 6, NULL, 0);
1653			else if (!strncmp(opt, "pciwr=", 6))
1654				pciwr = simple_strtoul(opt + 6, NULL, 0);
1655			else if (!strncmp(opt, "pcirr=", 6))
1656				pcirr = simple_strtoul(opt + 6, NULL, 0);
1657			else if (!strncmp(opt, "memsize=", 8))
1658				memsize = simple_strtoul(opt + 8, NULL, 0);
1659			else if (!strncmp(opt, "verbosity=", 10))
1660				verbosity = simple_strtoul(opt + 10, NULL, 0);
1661			else
1662				mode = opt;
1663		}
1664#endif
1665	output("CyblaFB version %s initializing\n", VERSION);
1666	return pci_register_driver(&cyblafb_pci_driver);
1667}
1668
1669static void __exit cyblafb_exit(void)
1670{
1671	pci_unregister_driver(&cyblafb_pci_driver);
1672}
1673
1674module_init(cyblafb_init);
1675module_exit(cyblafb_exit);
1676
1677MODULE_AUTHOR("Knut Petersen <knut_petersen@t-online.de>");
1678MODULE_DESCRIPTION("Framebuffer driver for Cyberblade/i1 graphics core");
1679MODULE_LICENSE("GPL");
1680