patch-2.3.25 linux/drivers/video/cyber2000fb.c

Next file: linux/drivers/video/cyber2000fb.h
Previous file: linux/drivers/video/clgenfb.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.24/linux/drivers/video/cyber2000fb.c linux/drivers/video/cyber2000fb.c
@@ -35,7 +35,9 @@
 #define DEFAULT_YRES	480
 #define DEFAULT_BPP	8
 
-static volatile unsigned char	*CyberRegs;
+#define MMIO_SIZE	0x000c0000
+
+static char			*CyberRegs;
 
 #include "cyber2000fb.h"
 
@@ -45,7 +47,7 @@
 static struct display_switch	*dispsw;
 static struct fb_var_screeninfo __initdata init_var = {};
 
-#ifdef DEBUG
+#if defined(DEBUG) && defined(CONFIG_DEBUG_LL)
 static void debug_printf(char *fmt, ...)
 {
 	char buffer[128];
@@ -61,168 +63,6 @@
 #define debug_printf(x...) do { } while (0)
 #endif
 
-/*
- *    Predefined Video Modes
- */
-static const struct res cyber2000_res[] = {
-	{
-		640, 480,
-		{
-			0x5f, 0x4f, 0x50, 0x80, 0x52, 0x9d, 0x0b, 0x3e,
-			0x00, 0x40,
-			0xe9, 0x8b, 0xdf, 0x50, 0x00, 0xe6, 0x04, 0xc3
-		},
-		0x00,
-		{ 0xd2, 0xce, 0xdb, 0x54 }
-	},
-
-	{
-		800, 600,
-		{
-			0x7f, 0x63, 0x64, 0x00, 0x66, 0x10, 0x6f, 0xf0,
-			0x00, 0x60,
-			0x5b, 0x8f, 0x57, 0x64, 0x00, 0x59, 0x6e, 0xe3
-		},
-		0x00,
-		{ 0x52, 0x85, 0xdb, 0x54 }
-	},
-
-	{
-		1024, 768,
-		{
-			0x9f, 0x7f, 0x80, 0x80, 0x8b, 0x94, 0x1e, 0xfd,
-			0x00, 0x60,
-			0x03, 0x86, 0xff, 0x80, 0x0f, 0x00, 0x1e, 0xe3
-		},
-		0x00,
-		{ 0xd0, 0x52, 0xdb, 0x54 }
-	},
-#if 0
-	{
-		1152, 886,
-		{
-		},
-		{
-		}
-	},
-#endif
-	{
-		1280, 1024,
-		{
-			0xce, 0x9f, 0xa0, 0x8f, 0xa2, 0x1f, 0x28, 0x52,
-			0x00, 0x40,
-			0x08, 0x8f, 0xff, 0xa0, 0x00, 0x03, 0x27, 0xe3
-		},
-		0x1d,
-		{ 0xb4, 0x4b, 0xdb, 0x54 }
-	},
-
-	{
-		1600, 1200,
-		{
-			0xff, 0xc7, 0xc9, 0x9f, 0xcf, 0xa0, 0xfe, 0x10,
- 			0x00, 0x40,
-			0xcf, 0x89, 0xaf, 0xc8, 0x00, 0xbc, 0xf1, 0xe3
-		},
-		0x1f,
-		{ 0xbd, 0x10, 0xdb, 0x54 }
-	}
-};
-
-#define NUM_TOTAL_MODES    arraysize(cyber2000_res)
-
-static const char igs_regs[] = {
-	0x10, 0x10,			0x12, 0x00,	0x13, 0x00,
-	0x30, 0x21,	0x31, 0x00,	0x32, 0x00,	0x33, 0x01,
-	0x50, 0x00,	0x51, 0x00,	0x52, 0x00,	0x53, 0x00,
-	0x54, 0x00,	0x55, 0x00,	0x56, 0x00,	0x57, 0x01,
-	0x58, 0x00,	0x59, 0x00,	0x5a, 0x00,
-	0x70, 0x0b,	0x71, 0x10,	0x72, 0x45,	0x73, 0x30,
-	0x74, 0x1b,	0x75, 0x1e,	0x76, 0x00,	0x7a, 0xc8
-};
-
-static const char crtc_idx[] = {
-	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-	0x08, 0x09,
-	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
-};
-
-static void cyber2000_init_hw(const struct res *res)
-{
-	int i;
-
-	debug_printf("init vga hw for %dx%d\n", res->xres, res->yres);
-
-	cyber2000_outb(0xef, 0x3c2);
-	cyber2000_crtcw(0x11, 0x0b);
-	cyber2000_attrw(0x11, 0x00);
-
-	cyber2000_seqw(0x00, 0x01);
-	cyber2000_seqw(0x01, 0x01);
-	cyber2000_seqw(0x02, 0x0f);
-	cyber2000_seqw(0x03, 0x00);
-	cyber2000_seqw(0x04, 0x0e);
-	cyber2000_seqw(0x00, 0x03);
-
-	for (i = 0; i < sizeof(crtc_idx); i++)
-		cyber2000_crtcw(crtc_idx[i], res->crtc_regs[i]);
-
-	for (i = 0x0a; i < 0x10; i++)
-		cyber2000_crtcw(i, 0);
-
-	cyber2000_crtcw(0x18, 0xff);
-
-	cyber2000_grphw(0x00, 0x00);
-	cyber2000_grphw(0x01, 0x00);
-	cyber2000_grphw(0x02, 0x00);
-	cyber2000_grphw(0x03, 0x00);
-	cyber2000_grphw(0x04, 0x00);
-	cyber2000_grphw(0x05, 0x60);
-	cyber2000_grphw(0x06, 0x05);
-	cyber2000_grphw(0x07, 0x0f);
-	cyber2000_grphw(0x08, 0xff);
-
-	for (i = 0; i < 16; i++)
-		cyber2000_attrw(i, i);
-
-	cyber2000_attrw(0x10, 0x01);
-	cyber2000_attrw(0x11, 0x00);
-	cyber2000_attrw(0x12, 0x0f);
-	cyber2000_attrw(0x13, 0x00);
-	cyber2000_attrw(0x14, 0x00);
-
-	for (i = 0; i < sizeof(igs_regs); i += 2)
-		cyber2000_grphw(igs_regs[i], igs_regs[i+1]);
-
-	cyber2000_grphw(0x11, res->crtc_ofl);
-
-	for (i = 0; i < 4; i += 1)
-		cyber2000_grphw(0xb0 + i, res->clk_regs[i]);
-
-	cyber2000_grphw(0x90, 0x01);
-	cyber2000_grphw(0xb9, 0x80);
-	cyber2000_grphw(0xb9, 0x00);
-
-	cyber2000_outb(0x56, 0x3ce);
-	i = cyber2000_inb(0x3cf);
-	cyber2000_outb(i | 4, 0x3cf);
-	cyber2000_outb(0x04, 0x3c6);
-	cyber2000_outb(i,    0x3cf);
-
-	cyber2000_outb(0x20, 0x3c0);
-	cyber2000_outb(0xff, 0x3c6);
-
-	for (i = 0; i < 256; i++) {
-		cyber2000_outb(i, 0x3c8);
-		cyber2000_outb(0, 0x3c9);
-		cyber2000_outb(0, 0x3c9);
-		cyber2000_outb(0, 0x3c9);
-	}
-}
-
-
-static struct fb_ops cyber2000fb_ops;
-
 /* -------------------- Hardware specific routines ------------------------- */
 
 /*
@@ -232,10 +72,10 @@
 {
 	int count = 10000;
 
-	while (cyber2000_inb(0xbf011) & 0x80) {
+	while (cyber2000_inb(CO_REG_CONTROL) & 0x80) {
 		if (!count--) {
 			debug_printf("accel_wait timed out\n");
-			cyber2000_outb(0, 0xbf011);
+			cyber2000_outb(0, CO_REG_CONTROL);
 			return;
 		}
 		udelay(10);
@@ -252,45 +92,54 @@
 cyber2000_accel_bmove(struct display *p, int sy, int sx, int dy, int dx,
 		int height, int width)
 {
-	unsigned long src, dst, chwidth = p->var.xres_virtual * fontheight(p);
-	int v = 0x8000;
+	unsigned long src, dst;
+	unsigned int fh, fw;
+	int cmd = CO_CMD_L_PATTERN_FGCOL;
+
+	fw    = fontwidth(p);
+	sx    *= fw;
+	dx    *= fw;
+	width *= fw;
+	width -= 1;
 
 	if (sx < dx) {
-		sx += width - 1;
-		dx += width - 1;
-		v |= 4;
+		sx += width;
+		dx += width;
+		cmd |= CO_CMD_L_INC_LEFT;
 	}
 
+	fh     = fontheight(p);
+	sy     *= fh;
+	dy     *= fh;
+	height *= fh;
+	height -= 1;
+
 	if (sy < dy) {
-		sy += height - 1;
-		dy += height - 1;
-		v |= 2;
+		sy += height;
+		dy += height;
+		cmd |= CO_CMD_L_INC_UP;
 	}
 
-	sx *= fontwidth(p);
-	dx *= fontwidth(p);
-	src = sx + sy * chwidth;
-	dst = dx + dy * chwidth;
-	width = width * fontwidth(p) - 1;
-	height = height * fontheight(p) - 1;
+	src    = sx + sy * p->var.xres_virtual;
+	dst    = dx + dy * p->var.xres_virtual;
 
 	cyber2000_accel_wait();
-	cyber2000_outb(0x00,   0xbf011);
-	cyber2000_outb(0x03,   0xbf048);
-	cyber2000_outw(width,  0xbf060);
+	cyber2000_outb(0x00,  CO_REG_CONTROL);
+	cyber2000_outb(0x03,  CO_REG_FORE_MIX);
+	cyber2000_outw(width, CO_REG_WIDTH);
 
 	if (p->var.bits_per_pixel != 24) {
-		cyber2000_outl(dst, 0xbf178);
-		cyber2000_outl(src, 0xbf170);
+		cyber2000_outl(dst, CO_REG_DEST_PTR);
+		cyber2000_outl(src, CO_REG_SRC_PTR);
 	} else {
-		cyber2000_outl(dst * 3, 0xbf178);
-		cyber2000_outb(dst, 0xbf078);
-		cyber2000_outl(src * 3, 0xbf170);
+		cyber2000_outl(dst * 3, CO_REG_DEST_PTR);
+		cyber2000_outb(dst,     CO_REG_X_PHASE);
+		cyber2000_outl(src * 3, CO_REG_SRC_PTR);
 	}
 
-	cyber2000_outw(height, 0xbf062);
-	cyber2000_outw(v,      0xbf07c);
-	cyber2000_outw(0x2800, 0xbf07e);
+	cyber2000_outw(height, CO_REG_HEIGHT);
+	cyber2000_outw(cmd,    CO_REG_CMD_L);
+	cyber2000_outw(0x2800, CO_REG_CMD_H);
 }
 
 static void
@@ -298,36 +147,40 @@
 		int height, int width)
 {
 	unsigned long dst;
+	unsigned int fw, fh;
 	u32 bgx = attr_bgcol_ec(p, conp);
 
-	dst = sx * fontwidth(p) + sy * p->var.xres_virtual * fontheight(p);
-	width = width * fontwidth(p) - 1;
-	height = height * fontheight(p) - 1;
+	fw = fontwidth(p);
+	fh = fontheight(p);
+
+	dst    = sx * fw + sy * p->var.xres_virtual * fh;
+	width  = width * fw - 1;
+	height = height * fh - 1;
 
 	cyber2000_accel_wait();
-	cyber2000_outb(0x00,   0xbf011);
-	cyber2000_outb(0x03,   0xbf048);
-	cyber2000_outw(width,  0xbf060);
-	cyber2000_outw(height, 0xbf062);
+	cyber2000_outb(0x00,   CO_REG_CONTROL);
+	cyber2000_outb(0x03,   CO_REG_FORE_MIX);
+	cyber2000_outw(width,  CO_REG_WIDTH);
+	cyber2000_outw(height, CO_REG_HEIGHT);
 
 	switch (p->var.bits_per_pixel) {
 	case 15:
 	case 16:
 		bgx = ((u16 *)p->dispsw_data)[bgx];
 	case 8:
-		cyber2000_outl(dst, 0xbf178);
+		cyber2000_outl(dst, CO_REG_DEST_PTR);
 		break;
 
 	case 24:
-		cyber2000_outl(dst * 3, 0xbf178);
-		cyber2000_outb(dst, 0xbf078);
+		cyber2000_outl(dst * 3, CO_REG_DEST_PTR);
+		cyber2000_outb(dst, CO_REG_X_PHASE);
 		bgx = ((u32 *)p->dispsw_data)[bgx];
 		break;
 	}
 
-	cyber2000_outl(bgx,    0xbf058);
-	cyber2000_outw(0x8000, 0xbf07c);
-	cyber2000_outw(0x0800, 0xbf07e);
+	cyber2000_outl(bgx, CO_REG_FOREGROUND);
+	cyber2000_outw(CO_CMD_L_PATTERN_FGCOL, CO_REG_CMD_L);
+	cyber2000_outw(0x0800, CO_REG_CMD_H);
 }
 
 static void
@@ -478,183 +331,136 @@
 	return 0;
 }
 
-static void cyber2000fb_calculate_timing(unsigned char *v, struct fb_var_screeninfo *var)
-{
-	int Htotal, Hdispend, Hblankstart, Hblankend, Hsyncstart, Hsyncend;
-	int Vtotal, Vdispend, Vblankstart, Vblankend, Vsyncstart, Vsyncend;
-#define BIT(v,b1,m,b2) (((v >> b1) & m) << b2)
-
-	Hdispend    = var->xres;
-	Hsyncstart  = var->xres + var->right_margin;
-	Hsyncend    = var->xres + var->right_margin + var->hsync_len;
-	Htotal      = var->xres + var->right_margin + var->hsync_len + var->left_margin;
-
-	Hblankstart = var->xres;
-	Hblankend   = Htotal - 4*8;
-
-	Vdispend    = var->yres;
-	Vsyncstart  = var->yres + var->lower_margin;
-	Vsyncend    = var->yres + var->lower_margin + var->vsync_len;
-	Vtotal      = var->yres + var->lower_margin + var->vsync_len + var->upper_margin;
-
-	Vblankstart = var->yres + 7;
-	Vblankend   = Vtotal - 11;
+struct par_info {
+	/*
+	 * Hardware
+	 */
+	unsigned char	clock_mult;
+	unsigned char	clock_div;
+	unsigned char	visualid;
+	unsigned char	pixformat;
+	unsigned char	crtc_ofl;
+	unsigned char	crtc[19];
+	unsigned int	width;
+	unsigned int	pitch;
 
-	Hdispend    >>= 3;
-	Hsyncstart  >>= 3;
-	Hsyncend    >>= 3;
-	Htotal      >>= 3;
-	Hblankstart >>= 3;
-	Hblankend   >>= 3;
-
-	Htotal      -= 5;
-	Hdispend    -= 1;
-	Vtotal	    -= 2;
-	Vdispend    -= 1;
-	Vblankstart -= 1;
-	Vblankend   -= 1;
-
-	v[0]  = Htotal;
-	v[1]  = Hdispend;
-	v[2]  = Hblankstart;
-	v[3]  = BIT(Hblankend,  0, 0x1f,  0) |
-		BIT(1,          0, 0x01,  7);
-	v[4]  = Hsyncstart;
-	v[5]  = BIT(Hsyncend,   0, 0x1f,  0) |
-	        BIT(Hblankend,  5, 0x01,  7);
-
-	v[6]  = Vtotal;
-	v[7]  = BIT(Vtotal,     8, 0x01,  0) |
-		BIT(Vdispend,   8, 0x01,  1) |
-		BIT(Vsyncstart, 8, 0x01,  2) |
-		BIT(Vblankstart,8, 0x01,  3) |
-		BIT(1,          0, 0x01,  4) |
-	        BIT(Vtotal,     9, 0x01,  5) |
-		BIT(Vdispend,   9, 0x01,  6) |
-		BIT(Vsyncstart, 9, 0x01,  7);
-	v[8]  = 0;
-	v[9]  = BIT(0,          0, 0x1f,  0) |
-	        BIT(Vblankstart,9, 0x01,  5) |
-		BIT(1,          0, 0x01,  6);
-	v[10] = Vsyncstart;
-	v[11] = BIT(Vsyncend,   0, 0x0f,  0) |
-		BIT(1,          0, 0x01,  7);
-	v[12] = Vdispend;
-	v[14] = 0;
-	v[15] = Vblankstart;
-	v[16] = Vblankend;
-	v[17] = 0xe3;
+	/*
+	 * Other
+	 */
+	unsigned int	visual;
+};
 
-	/* overflow - graphics reg 0x11 */
-	v[18] = BIT(Vtotal,     10, 0x01,  0) | /* guess */
-		BIT(Vdispend,   10, 0x01,  1) |
-		BIT(Vsyncstart, 10, 0x01,  2) | /* guess */
-		BIT(Vblankstart,10, 0x01,  3) | /* guess */
-		BIT(Hblankend,   6, 0x01,  4);  /* guess */
-}
+static const char crtc_idx[] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18
+};
 
-static void cyber2000fb_set_timing(struct fb_var_screeninfo *var)
+static void cyber2000fb_set_timing(struct par_info *hw)
 {
-	unsigned int width = var->xres_virtual;
-	unsigned int scr_pitch, fetchrow, i;
-	char b, graph_r77, crtc[32];
+	unsigned int fetchrow, i;
 
-	switch (var->bits_per_pixel) {
-	case 8:	/* PSEUDOCOLOUR, 256 */
-		b = 0;
-		graph_r77 = 1;
-		scr_pitch = width;
-		break;
+	/*
+	 * Blank palette
+	 */
+	for (i = 0; i < 256; i++) {
+		cyber2000_outb(i, 0x3c8);
+		cyber2000_outb(0, 0x3c9);
+		cyber2000_outb(0, 0x3c9);
+		cyber2000_outb(0, 0x3c9);
+	}
 
-	case 15:/* DIRECTCOLOUR, 32k */
-		b = 1;
-		graph_r77 = 6;
-		scr_pitch = width * 2;
-		break;
+	cyber2000_outb(0xef, 0x3c2);
+	cyber2000_crtcw(0x11, 0x0b);
+	cyber2000_attrw(0x11, 0x00);
 
-	case 16:/* DIRECTCOLOUR, 64k */
-		b = 1;
-		graph_r77 = 2;
-		scr_pitch = width * 2;
-		break;
+	cyber2000_seqw(0x00, 0x01);
+	cyber2000_seqw(0x01, 0x01);
+	cyber2000_seqw(0x02, 0x0f);
+	cyber2000_seqw(0x03, 0x00);
+	cyber2000_seqw(0x04, 0x0e);
+	cyber2000_seqw(0x00, 0x03);
 
-	case 24:/* TRUECOLOUR, 16m */
-		b = 2;
-		graph_r77 = 4;
-		width *= 3;
-		scr_pitch = width;
-		break;
+	for (i = 0; i < sizeof(crtc_idx); i++)
+		cyber2000_crtcw(crtc_idx[i], hw->crtc[i]);
 
-	default:
-		return;
-	}
+	for (i = 0x0a; i < 0x10; i++)
+		cyber2000_crtcw(i, 0);
 
-	width -= 1;
-	scr_pitch >>= 3;
-	fetchrow = scr_pitch + 1;
+	cyber2000_grphw(0x11, hw->crtc_ofl);
+	cyber2000_grphw(0x00, 0x00);
+	cyber2000_grphw(0x01, 0x00);
+	cyber2000_grphw(0x02, 0x00);
+	cyber2000_grphw(0x03, 0x00);
+	cyber2000_grphw(0x04, 0x00);
+	cyber2000_grphw(0x05, 0x60);
+	cyber2000_grphw(0x06, 0x05);
+	cyber2000_grphw(0x07, 0x0f);
+	cyber2000_grphw(0x08, 0xff);
 
-	cyber2000fb_calculate_timing(crtc, var);
+	/* Attribute controller registers */
+	for (i = 0; i < 16; i++)
+		cyber2000_attrw(i, i);
 
-	for (i = 0; i < NUM_TOTAL_MODES; i++)
-		if (var->xres == cyber2000_res[i].xres &&
-		    var->yres == cyber2000_res[i].yres)
-			break;
+	cyber2000_attrw(0x10, 0x01);
+	cyber2000_attrw(0x11, 0x00);
+	cyber2000_attrw(0x12, 0x0f);
+	cyber2000_attrw(0x13, 0x00);
+	cyber2000_attrw(0x14, 0x00);
 
-	if (i < NUM_TOTAL_MODES)
-		cyber2000_init_hw(cyber2000_res + i);
+	/* PLL registers */
+	cyber2000_grphw(0xb0, hw->clock_mult);
+	cyber2000_grphw(0xb1, hw->clock_div);
+	cyber2000_grphw(0xb2, 0xdb);
+	cyber2000_grphw(0xb3, 0x54);		/* MCLK: 75MHz */
+	cyber2000_grphw(0x90, 0x01);
+	cyber2000_grphw(0xb9, 0x80);
+	cyber2000_grphw(0xb9, 0x00);
 
-	crtc[13] = scr_pitch;
+	cyber2000_outb(0x56, 0x3ce);
+	i = cyber2000_inb(0x3cf);
+	cyber2000_outb(i | 4, 0x3cf);
+	cyber2000_outb(0x04, 0x3c6);
+	cyber2000_outb(i,    0x3cf);
 
-	/*
-	 * reprogram the CRTC with the values we calculated
-	 * above.  This should be cleaned up once we're
-	 * confident that we're generating the correct
-	 * values.  Disable this if you're having problems,
-	 * and report the values obtained from the kernel
-	 * messages.
-	 */
-#if 1
-	cyber2000_crtcw(0x11, 0x0b);
-	for (i = 0; i < sizeof(crtc_idx); i++)
-		cyber2000_crtcw(crtc_idx[i], crtc[i]);
-#else
-	cyber2000_crtcw(0x13, crtc[13]);
-#endif
+	cyber2000_outb(0x20, 0x3c0);
+	cyber2000_outb(0xff, 0x3c6);
 
+	fetchrow = hw->pitch + 1;
 	cyber2000_grphw(0x14, fetchrow);
-					/* FIXME: is this the right way round? */
-	cyber2000_grphw(0x15, ((fetchrow >> 4) & 0xf0) | ((scr_pitch >> 8) & 0x0f));
-	cyber2000_grphw(0x77, graph_r77);
+	/* FIXME: is this the right way round? */
+	cyber2000_grphw(0x15, ((fetchrow >> 4) & 0xf0) | ((hw->pitch >> 8) & 0x0f));
+	cyber2000_grphw(0x77, hw->visualid);
 	cyber2000_grphw(0x33, 0x1c);
 
-	cyber2000_outw(width, 0xbf018);
-	cyber2000_outw(width, 0xbf218);
-	cyber2000_outb(b,     0xbf01c);
-
-#if 0
-{ int j; i = 0;
- printk(KERN_DEBUG);
- for (j = 0; j < 19; j++) printk("%2d ", j); printk("\n"KERN_DEBUG);
- for (j = 0; j < 19; j++) printk("%02X ", crtc[j]); printk("\n"KERN_DEBUG);
- for (j = 0; j < 18; j++) printk("%02X ", cyber2000_res[i].crtc_regs[j]);
- printk("%02X\n", cyber2000_res[i].crtc_ofl);
-}
-#endif
+	/*
+	 * Set up accelerator registers
+	 */
+	cyber2000_outw(hw->width, CO_REG_SRC_WIDTH);
+	cyber2000_outw(hw->width, CO_REG_DEST_WIDTH);
+	cyber2000_outb(hw->pixformat, CO_REG_PIX_FORMAT);
 }
 
-static inline void
+static inline int
 cyber2000fb_update_start(struct fb_var_screeninfo *var)
 {
-#if 0
 	unsigned int base;
 
 	base = var->yoffset * var->xres_virtual + var->xoffset;
 
-	cyber2000_crtcw(0x0c, base);
-	cyber2000_crtcw(0x0d, base >> 8);
-	/* FIXME: need the upper bits of the start offset */
-/*	cyber2000_crtcw(0x??, base >> 16);*/
-#endif
+	base >>= 2;
+
+	if (base >= 1 << 20)
+		return -EINVAL;
+
+	/*
+	 * FIXME: need the upper bits of the start offset
+	 */
+	cyber2000_grphw(0x10, base >> 16 | 0x10);
+	cyber2000_crtcw(0x0c, base >> 8);
+	cyber2000_crtcw(0x0d, base);
+
+	return 0;
 }
 
 /*
@@ -723,30 +529,230 @@
 	return err;
 }
 
+static int cyber2000fb_decode_crtc(struct par_info *hw, struct fb_var_screeninfo *var)
+{
+	unsigned int Htotal, Hblankend, Hsyncend;
+	unsigned int Vtotal, Vdispend, Vblankstart, Vblankend, Vsyncstart, Vsyncend;
+#define BIT(v,b1,m,b2) (((v >> b1) & m) << b2)
+
+	hw->crtc[13] = hw->pitch;
+	hw->crtc[17] = 0xe3;
+	hw->crtc[14] = 0;
+	hw->crtc[8]  = 0;
+
+	Htotal      = var->xres + var->right_margin + var->hsync_len + var->left_margin;
+	if (Htotal > 2080)
+		return -EINVAL;
+
+	hw->crtc[0] = (Htotal >> 3) - 5;			/* Htotal	*/
+	hw->crtc[1] = (var->xres >> 3) - 1;			/* Hdispend	*/
+	hw->crtc[2] = var->xres >> 3;				/* Hblankstart	*/
+	hw->crtc[4] = (var->xres + var->right_margin) >> 3;	/* Hsyncstart	*/
+
+	Hblankend   = (Htotal - 4*8) >> 3;
+
+	hw->crtc[3] = BIT(Hblankend,  0, 0x1f,  0) |		/* Hblankend	*/
+		      BIT(1,          0, 0x01,  7);
+
+	Hsyncend    = (var->xres + var->right_margin + var->hsync_len) >> 3;
+
+	hw->crtc[5] = BIT(Hsyncend,   0, 0x1f,  0) |		/* Hsyncend	*/
+		      BIT(Hblankend,  5, 0x01,  7);
+
+	Vdispend    = var->yres - 1;
+	Vsyncstart  = var->yres + var->lower_margin;
+	Vsyncend    = var->yres + var->lower_margin + var->vsync_len;
+	Vtotal      = var->yres + var->lower_margin + var->vsync_len + var->upper_margin - 2;
+
+	if (Vtotal > 2047)
+		return -EINVAL;
+
+	Vblankstart = var->yres + 6;
+	Vblankend   = Vtotal - 10;
+
+	hw->crtc[6]  = Vtotal;
+	hw->crtc[7]  = BIT(Vtotal,     8, 0x01,  0) |
+			BIT(Vdispend,   8, 0x01,  1) |
+			BIT(Vsyncstart, 8, 0x01,  2) |
+			BIT(Vblankstart,8, 0x01,  3) |
+			BIT(1,          0, 0x01,  4) |
+	        	BIT(Vtotal,     9, 0x01,  5) |
+			BIT(Vdispend,   9, 0x01,  6) |
+			BIT(Vsyncstart, 9, 0x01,  7);
+	hw->crtc[9]  = BIT(0,          0, 0x1f,  0) |
+		        BIT(Vblankstart,9, 0x01,  5) |
+			BIT(1,          0, 0x01,  6);
+	hw->crtc[10] = Vsyncstart;
+	hw->crtc[11] = BIT(Vsyncend,   0, 0x0f,  0) |
+		       BIT(1,          0, 0x01,  7);
+	hw->crtc[12] = Vdispend;
+	hw->crtc[15] = Vblankstart;
+	hw->crtc[16] = Vblankend;
+	hw->crtc[18] = 0xff;
+
+	/* overflow - graphics reg 0x11 */
+/* 0=VTOTAL:10 1=VDEND:10 2=VRSTART:10 3=VBSTART:10 4=LINECOMP:10 5-IVIDEO 6=FIXCNT */
+	hw->crtc_ofl =
+		BIT(Vtotal,     10, 0x01,  0) |
+		BIT(Vdispend,   10, 0x01,  1) |
+		BIT(Vsyncstart, 10, 0x01,  2) |
+		BIT(Vblankstart,10, 0x01,  3) |
+		1 << 4;
+
+	return 0;
+}
+
+/*
+ * The following was discovered by a good monitor,
+ * bit twiddling, theorising and but mostly luck.
+ * Strangely, it looks like everyone elses' PLL!
+ *
+ * Clock registers:
+ *   fclock = fpll / div2
+ *   fpll   = fref * mult / div1
+ * where:
+ *   fref = 14.318MHz (69842ps)
+ *   mult = reg0xb0.7:0
+ *   div1 = (reg0xb1.5:0 + 1)
+ *   div2 =  2^(reg0xb1.7:6)
+ *   fpll should be between 150 and 220 MHz
+ *  (6667ps and 4545ps)
+ */
 static int
-cyber2000fb_decode_var(struct fb_var_screeninfo *var, int con, int *visual)
+cyber2000fb_decode_clock(struct par_info *hw, struct fb_var_screeninfo *var)
 {
+	unsigned long pll_ps = var->pixclock;
+	unsigned long ref_ps = 69842;
+	int div2, div1, mult;
+
+	/*
+	 * Step 1:
+	 *   find div2 such that 150MHz < fpll < 220MHz
+	 *   and 0 <= div2 < 4
+	 */
+	for (div2 = 0; div2 < 4; div2++, pll_ps >>= 1)
+		if (6667 > pll_ps && pll_ps > 4545)
+			break;
+
+	if (div2 == 4)
+		return -EINVAL;
+
+#if 0
+	/*
+	 * Step 2:
+	 *  Find fpll
+	 *    fpll = fref * mult / div1
+	 *
+	 * Note!  This just picks any old values at the moment,
+	 * and as such I don't trust it.  It certainly doesn't
+	 * come out with the values below, so the PLL may become
+	 * unstable under some circumstances (you don't want an
+	 * FM dot clock)
+	 */
+	for (div1 = 32; div1 > 1; div1 -= 1) {
+		mult = (ref_ps * div1 + pll_ps / 2) / pll_ps;
+		if (mult < 256)
+			break;
+	}
+#else
+	if (pll_ps == 4630) {		/* 216.0, 108.0, 54.00, 27.000 */
+		mult = 181;		/* 4630   9260   18520  37040  */
+		div1 = 12;
+	} else if (pll_ps == 4965) {	/* 201.0, 100.5, 50.25, 25.125 */
+		mult = 211;		/* 4965   9930   19860  39720  */
+		div1 = 15;
+	} else if (pll_ps == 5050) {	/* 198.0,  99.0, 49.50, 24.750 */
+		mult = 83;		/* 5050   10100  20200  40400  */
+		div1 = 6;
+	} else if (pll_ps == 6349) {	/* 158.0,  79.0, 39.50, 19.750 */
+		mult = 209;		/* 6349   12698  25396  50792  */
+		div1 = 19;
+	} else if (pll_ps == 6422) {	/* 156.0,  78.0, 39.00, 19.500 */
+		mult = 190;		/* 6422   12844  25688  51376  */
+		div1 = 17;
+	} else
+		return -EINVAL;
+#endif
+	/*
+	 * Step 3:
+	 *  combine values
+	 */
+	hw->clock_mult = mult - 1;
+	hw->clock_div  = div2 << 6 | (div1 - 1);
+
+	return 0;
+}
+
+/*
+ * Decode the info required for the hardware.
+ * This involves the PLL parameters for the dot clock,
+ * CRTC registers, and accelerator settings.
+ */
+static int
+cyber2000fb_decode_var(struct fb_var_screeninfo *var, int con, struct par_info *hw)
+{
+	int err;
+
+	hw->width = var->xres_virtual;
 	switch (var->bits_per_pixel) {
 #ifdef FBCON_HAS_CFB8
-	case 8:
-		*visual = FB_VISUAL_PSEUDOCOLOR;
+	case 8:	/* PSEUDOCOLOUR, 256 */
+		hw->visual    = FB_VISUAL_PSEUDOCOLOR;
+		hw->pixformat = PIXFORMAT_8BPP;
+		hw->visualid  = VISUALID_256;
+		hw->pitch     = hw->width >> 3;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB16
-	case 15:
-	case 16:
-		*visual = FB_VISUAL_DIRECTCOLOR;
+	case 15:/* DIRECTCOLOUR, 32k */
+		hw->visual    = FB_VISUAL_DIRECTCOLOR;
+		hw->pixformat = PIXFORMAT_16BPP;
+		hw->visualid  = VISUALID_32K;
+		hw->pitch     = hw->width >> 2;
+		break;
+
+	case 16:/* DIRECTCOLOUR, 64k */
+		hw->visual    = FB_VISUAL_DIRECTCOLOR;
+		hw->pixformat = PIXFORMAT_16BPP;
+		hw->visualid  = VISUALID_64K;
+		hw->pitch     = hw->width >> 2;
 		break;
 #endif
 #ifdef FBCON_HAS_CFB24
-	case 24:
-		*visual = FB_VISUAL_TRUECOLOR;
+	case 24:/* TRUECOLOUR, 16m */
+		hw->visual    = FB_VISUAL_TRUECOLOR;
+		hw->pixformat = PIXFORMAT_24BPP;
+		hw->visualid  = VISUALID_16M;
+		hw->width    *= 3;
+		hw->pitch     = hw->width >> 3;
 		break;
 #endif
 	default:
 		return -EINVAL;
 	}
 
+	err = cyber2000fb_decode_clock(hw, var);
+	if (err)
+		return err;
+
+	err = cyber2000fb_decode_crtc(hw, var);
+	if (err)
+		return err;
+
+	debug_printf("Clock: %02X %02X\n",
+		hw->clock_mult, hw->clock_div);
+	{
+		int i;
+
+		for (i = 0; i < 19; i++)
+			debug_printf("%2d ", i);
+		debug_printf("\n");
+		for (i = 0; i < 18; i++)
+			debug_printf("%02X ", hw->crtc[i]);
+		debug_printf("%02X\n", hw->crtc_ofl);
+	}
+	hw->width     -= 1;
+
 	return 0;
 }
 
@@ -760,7 +766,7 @@
 	struct display *display;
 
 	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
-	strcpy(fix->id, "Cyber2000");
+	strcpy(fix->id, current_par.dev_name);
 
 	if (con >= 0)
 		display = fb_display + con;
@@ -770,7 +776,7 @@
 	fix->smem_start	 = current_par.screen_base_p;
 	fix->smem_len	 = current_par.screen_size;
 	fix->mmio_start	 = current_par.regs_base_p;
-	fix->mmio_len	 = 0x000c0000;
+	fix->mmio_len	 = MMIO_SIZE;
 	fix->type	 = display->type;
 	fix->type_aux	 = display->type_aux;
 	fix->xpanstep	 = 0;
@@ -806,14 +812,15 @@
 cyber2000fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
 {
 	struct display *display;
-	int err, chgvar = 0, visual;
+	struct par_info hw;
+	int err, chgvar = 0;
 
 	if (con >= 0)
 		display = fb_display + con;
 	else
 		display = &global_disp;
 
-	err = cyber2000fb_decode_var(var, con, &visual);
+	err = cyber2000fb_decode_var(var, con, &hw);
 	if (err)
 		return err;
 
@@ -854,11 +861,11 @@
 	if (var->activate & FB_ACTIVATE_ALL)
 		global_disp.var = display->var;
 
-	display->screen_base	= (char *)current_par.screen_base;
-	display->visual		= visual;
+	display->screen_base	= current_par.screen_base;
+	display->visual		= hw.visual;
 	display->type		= FB_TYPE_PACKED_PIXELS;
 	display->type_aux	= 0;
-	display->ypanstep	= 0;
+	display->ypanstep	= 1;
 	display->ywrapstep	= 0;
 	display->can_soft_blank = 1;
 	display->inverse	= 0;
@@ -887,8 +894,8 @@
 		break;
 #endif
 	default:
-		printk(KERN_WARNING "cyber2000: no support for %dbpp\n",
-		       display->var.bits_per_pixel);
+		printk(KERN_WARNING "%s: no support for %dbpp\n",
+		       current_par.dev_name, display->var.bits_per_pixel);
 		dispsw = &fbcon_dummy;
 		break;
 	}
@@ -908,7 +915,7 @@
 		struct fb_cmap *cmap;
 
 		cyber2000fb_update_start(var);
-		cyber2000fb_set_timing(var);
+		cyber2000fb_set_timing(&hw);
 
 		if (display->cmap.len)
 			cmap = &display->cmap;
@@ -938,10 +945,9 @@
 		return -EINVAL;
 	if (y_bottom > fb_display[con].var.yres_virtual)
 		return -EINVAL;
-/*disabled until we can update the start address properly */
-return -EINVAL;
 
-	cyber2000fb_update_start(var);
+	if (cyber2000fb_update_start(var))
+		return -EINVAL;
 
 	fb_display[con].var.xoffset = var->xoffset;
 	fb_display[con].var.yoffset = var->yoffset;
@@ -970,9 +976,12 @@
 static int
 cyber2000fb_updatevar(int con, struct fb_info *info)
 {
+	int ret = 0;
+
 	if (con == current_par.currcon)
-		cyber2000fb_update_start(&fb_display[con].var);
-	return 0;
+		ret = cyber2000fb_update_start(&fb_display[con].var);
+
+	return ret;
 }
 
 static int
@@ -1020,11 +1029,6 @@
 	}
 }
 
-int __init cyber2000fb_setup(char *options)
-{
-	return 0;
-}
-
 static struct fb_ops cyber2000fb_ops =
 {
 	cyber2000fb_open,
@@ -1038,6 +1042,27 @@
 	cyber2000fb_ioctl
 };
 
+/*
+ * These parameters give
+ * 640x480, hsync 31.5kHz, vsync 60Hz
+ */
+static struct fb_videomode __initdata
+cyber2000fb_default_mode = {
+	name:		NULL,
+	refresh:	60,
+	xres:		640,
+	yres:		480,
+	pixclock:	39722,
+	left_margin:	56,
+	right_margin:	16,
+	upper_margin:	34,
+	lower_margin:	9,
+	hsync_len:	88,
+	vsync_len:	2,
+	sync:		FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	vmode:		FB_VMODE_NONINTERLACED
+};
+
 static void __init 
 cyber2000fb_init_fbinfo(void)
 {
@@ -1063,23 +1088,6 @@
 	 * setup initial parameters
 	 */
 	memset(&init_var, 0, sizeof(init_var));
-	init_var.xres_virtual		=
-	init_var.xres			= DEFAULT_XRES;
-	init_var.yres_virtual		=
-	init_var.yres			= DEFAULT_YRES;
-	init_var.bits_per_pixel		= DEFAULT_BPP;
-
-	/*
-	 * These parameters give
-	 * 640x480, hsync 31.5kHz, vsync 60Hz
-	 */
-	init_var.left_margin		= 56;
-	init_var.right_margin		= 16;
-	init_var.upper_margin		= 34;
-	init_var.lower_margin		= 9;
-	init_var.hsync_len		= 88;
-	init_var.vsync_len		= 2;
-	init_var.pixclock		= 39722;
 
 	init_var.red.msb_right		= 0;
 	init_var.green.msb_right	= 0;
@@ -1134,8 +1142,81 @@
 	init_var.height			= -1;
 	init_var.width			= -1;
 	init_var.accel_flags		= FB_ACCELF_TEXT;
-	init_var.sync			= FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT;
-	init_var.vmode			= FB_VMODE_NONINTERLACED;
+}
+
+/*
+ * Cyber2000 options:
+ *
+ *  font:fontname
+ *	Set the fontname
+ *
+ *  res:XxY
+ *	Set the default display resolution
+ */
+static void __init
+cyber2000fb_parse_font(char *opt)
+{
+	strcpy(fb_info.fontname, opt);
+}
+
+static struct options {
+	char *name;
+	void (*parse)(char *opt);
+} opt_table[] __initdata = {
+	{ "font",	cyber2000fb_parse_font		},
+	{ NULL,		NULL				}
+};
+
+int __init
+cyber2000fb_setup(char *options)
+{
+	struct options *optp;
+	char *opt;
+
+	if (!options || !*options)
+		return 0;
+
+	cyber2000fb_init_fbinfo();
+
+	for (opt = strtok(options, ","); opt; opt = strtok(NULL, ",")) {
+		if (!*opt)
+			continue;
+
+		for (optp = opt_table; optp->name; optp++) {
+			int optlen;
+
+			optlen = strlen(optp->name);
+
+			if (strncmp(opt, optp->name, optlen) == 0 &&
+			    opt[optlen] == ':') {
+				optp->parse(opt + optlen + 1);
+				break;
+			}
+		}
+
+		if (!optp->name)
+			printk(KERN_ERR "CyberPro20x0: unknown parameter: %s\n",
+				opt);
+	}
+	return 0;
+}
+
+static char igs_regs[] __initdata = {
+	0x10, 0x10,			0x12, 0x00,	0x13, 0x00,
+/*	0x30, 0x21,*/	0x31, 0x00,	0x32, 0x00,	0x33, 0x01,
+	0x50, 0x00,	0x51, 0x00,	0x52, 0x00,	0x53, 0x00,
+	0x54, 0x00,	0x55, 0x00,	0x56, 0x00,	0x57, 0x01,
+	0x58, 0x00,	0x59, 0x00,	0x5a, 0x00,
+	0x70, 0x0b,/*	0x71, 0x10,	0x72, 0x45,*/	0x73, 0x30,
+	0x74, 0x1b,	0x75, 0x1e,	0x76, 0x00,	0x7a, 0xc8
+};
+
+static void __init cyber2000fb_hw_init(void)
+{
+	int i;
+
+	for (i = 0; i < sizeof(igs_regs); i += 2)
+		cyber2000_grphw(igs_regs[i], igs_regs[i+1]);
 }
 
 /*
@@ -1145,37 +1226,96 @@
 {
 	struct pci_dev *dev;
 	u_int h_sync, v_sync;
-	u_long base_p, base_v;
+	u_long mmio_base, smem_base, smem_size;
+	int err = 0;
+
+	dev = pci_find_device(PCI_VENDOR_ID_INTERG,
+			      PCI_DEVICE_ID_INTERG_2000, NULL);
+
+	if (!dev)
+		dev = pci_find_device(PCI_VENDOR_ID_INTERG,
+				      PCI_DEVICE_ID_INTERG_2010, NULL);
 
-	dev = pci_find_device(PCI_VENDOR_ID_INTERG, 0x2000, NULL);
 	if (!dev)
 		return -ENXIO;
 
-	/* this should be done by PCI generic code */
-	base_p = 0x80000000 + dev->resource[0].start;
+	sprintf(current_par.dev_name, "CyberPro%4X", dev->device);
+
+	smem_base = dev->resource[0].start;
+	mmio_base = dev->resource[0].start + 0x00800000;
 
 	/*
-	 * This should be ioremap'd, thus:
-	 *
-	 * base_v = ioremap(dev->resource[0].start, dev->resource[0].end - dev->resource[0].start + 1);
+	 * Map in the registers
 	 */
-	base_v = (u_long)bus_to_virt(dev->resource[0].start);
+	if (!request_mem_region(mmio_base, MMIO_SIZE, "memory mapped I/O")) {
+		printk("%s: memory mapped IO in use\n",
+		       current_par.dev_name);
+		return -EBUSY;
+	}
 
-	/*FIXME*/
-	CyberRegs = base_v + 0x00800000;
+	CyberRegs = ioremap(mmio_base, MMIO_SIZE);
+	if (!CyberRegs) {
+		printk("%s: unable to map memory mapped IO\n",
+		       current_par.dev_name);
+		err = -ENOMEM;
+		goto release_mmio_resource;
+	}
 
 	cyber2000_outb(0x18, 0x46e8);
 	cyber2000_outb(0x01, 0x102);
 	cyber2000_outb(0x08, 0x46e8);
 
+	/*
+	 * get the video RAM size from the VGA register.
+	 * This should have been already initialised by the BIOS,
+	 * but if it's garbage, claim default 1MB VRAM (woody)
+	 */
+	cyber2000_outb(0x72, 0x3ce);
+	switch (cyber2000_inb(0x3cf) & 3) {
+	case 2:	 smem_size = 0x00400000; break;
+	case 1:	 smem_size = 0x00200000; break;
+	default: smem_size = 0x00100000; break;
+	}
+
+	/*
+	 * Map in screen memory
+	 */
+	if (!request_mem_region(smem_base, smem_size, "frame buffer")) {
+		printk("%s: frame buffer in use\n",
+		       current_par.dev_name);
+		err = -EBUSY;
+		goto release_mmio;
+	}
+
+	current_par.screen_base = ioremap(smem_base, smem_size);
+	if (!current_par.screen_base) {
+		printk("%s: unable to map screen memory\n",
+		       current_par.dev_name);
+		err = -ENOMEM;
+		goto release_smem_resource;
+	}
+current_par.screen_base += IO_FUDGE_FACTOR;
+	current_par.screen_size   = smem_size;
+	current_par.screen_base_p = smem_base + 0x80000000;
+	current_par.regs_base_p   = mmio_base + 0x80000000;
+	current_par.currcon	  = -1;
+
 	cyber2000fb_init_fbinfo();
 
-	current_par.currcon		= -1;
-	current_par.screen_base_p	= base_p;
-	current_par.screen_base		= base_v;
-	current_par.screen_size		= 0x00200000;
-	current_par.regs_base_p		= base_p + 0x00800000;
+	if (!fb_find_mode(&init_var, &fb_info, NULL,
+	    NULL, 0, &cyber2000fb_default_mode, 8)) {
+		printk("%s: no valid mode found\n",
+			current_par.dev_name);
+		goto release_smem_resource;
+	}
+
+	init_var.yres_virtual = smem_size * 8 /
+			(init_var.bits_per_pixel * init_var.xres_virtual);
 
+	if (init_var.yres_virtual < init_var.yres)
+		init_var.yres_virtual = init_var.yres;
+    
+	cyber2000fb_hw_init();
 	cyber2000fb_set_var(&init_var, -1, &fb_info);
 
 	h_sync = 1953125000 / init_var.pixclock;
@@ -1184,24 +1324,44 @@
 	v_sync = h_sync / (init_var.yres + init_var.upper_margin +
 		 init_var.lower_margin + init_var.vsync_len);
 
-	printk("Cyber2000: %ldkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
+	printk("%s: %ldkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
+		current_par.dev_name,
 		current_par.screen_size >> 10,
 		init_var.xres, init_var.yres,
 		h_sync / 1000, h_sync % 1000, v_sync);
 
-	if (register_framebuffer(&fb_info) < 0)
-		return -EINVAL;
+	if (register_framebuffer(&fb_info) < 0) {
+		err = -EINVAL;
+		goto release_smem;
+	}
+
+	current_par.initialised = 1;
 
 	MOD_INC_USE_COUNT;	/* TODO: This driver cannot be unloaded yet */
 	return 0;
-}
 
+release_smem:
+	iounmap(current_par.screen_base);
+release_smem_resource:
+	release_mem_region(smem_base, smem_size);
+release_mmio:
+	iounmap(CyberRegs);
+release_mmio_resource:
+	release_mem_region(mmio_base, MMIO_SIZE);
 
+	return err;
+}
 
 #ifdef MODULE
 int __init init_module(void)
 {
-	return cyber2000fb_init();
+	int ret;
+
+	ret = cyber2000fb_init();
+	if (ret)
+		return ret;
+
+	return 0;
 }
 
 void cleanup_module(void)
@@ -1210,6 +1370,12 @@
 	   decremented to zero */
 	unregister_framebuffer(&fb_info);
 	/* TODO: clean up ... */
+
+	iounmap(current_par.screen_base);
+	iounmap(CyberRegs);
+
+	release_mem_region(smem_base, current_par.screen_size);
+	release_mem_region(mmio_base, MMIO_SIZE);
 }
 
 #endif				/* MODULE */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)