xref: /linux/drivers/video/fbdev/vga16fb.c (revision 1260ed77798502de9c98020040d2995008de10cc)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/drivers/video/vga16.c -- VGA 16-color framebuffer driver
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
51da177e4SLinus Torvalds  * Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm
61da177e4SLinus Torvalds  * Based on VESA framebuffer (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * This file is subject to the terms and conditions of the GNU General
91da177e4SLinus Torvalds  * Public License.  See the file COPYING in the main directory of this
101da177e4SLinus Torvalds  * archive for more details.
111da177e4SLinus Torvalds  */
121da177e4SLinus Torvalds 
132cb14c86SThomas Zimmermann #include <linux/aperture.h>
141da177e4SLinus Torvalds #include <linux/module.h>
151da177e4SLinus Torvalds #include <linux/kernel.h>
161da177e4SLinus Torvalds #include <linux/errno.h>
171da177e4SLinus Torvalds #include <linux/string.h>
181da177e4SLinus Torvalds #include <linux/mm.h>
191da177e4SLinus Torvalds #include <linux/delay.h>
201da177e4SLinus Torvalds #include <linux/fb.h>
211da177e4SLinus Torvalds #include <linux/ioport.h>
221da177e4SLinus Torvalds #include <linux/init.h>
23120ddb41SAntonino A. Daplas #include <linux/platform_device.h>
24a8f340e3SJon Smirl #include <linux/screen_info.h>
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds #include <asm/io.h>
271da177e4SLinus Torvalds #include <video/vga.h>
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds #define MODE_SKIP4	1
301da177e4SLinus Torvalds #define MODE_8BPP	2
311da177e4SLinus Torvalds #define MODE_CFB	4
321da177e4SLinus Torvalds #define MODE_TEXT	8
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds /* --------------------------------------------------------------------- */
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds /*
371da177e4SLinus Torvalds  * card parameters
381da177e4SLinus Torvalds  */
391da177e4SLinus Torvalds 
40120ddb41SAntonino A. Daplas struct vga16fb_par {
411da177e4SLinus Torvalds 	/* structure holding original VGA register settings when the
421da177e4SLinus Torvalds            screen is blanked */
431da177e4SLinus Torvalds 	struct {
441da177e4SLinus Torvalds 		unsigned char	SeqCtrlIndex;	  /* Sequencer Index reg.   */
451da177e4SLinus Torvalds 		unsigned char	CrtCtrlIndex;	  /* CRT-Contr. Index reg.  */
461da177e4SLinus Torvalds 		unsigned char	CrtMiscIO;	  /* Miscellaneous register */
471da177e4SLinus Torvalds 		unsigned char	HorizontalTotal;  /* CRT-Controller:00h */
481da177e4SLinus Torvalds 		unsigned char	HorizDisplayEnd;  /* CRT-Controller:01h */
491da177e4SLinus Torvalds 		unsigned char	StartHorizRetrace;/* CRT-Controller:04h */
501da177e4SLinus Torvalds 		unsigned char	EndHorizRetrace;  /* CRT-Controller:05h */
511da177e4SLinus Torvalds 		unsigned char	Overflow;	  /* CRT-Controller:07h */
521da177e4SLinus Torvalds 		unsigned char	StartVertRetrace; /* CRT-Controller:10h */
531da177e4SLinus Torvalds 		unsigned char	EndVertRetrace;	  /* CRT-Controller:11h */
541da177e4SLinus Torvalds 		unsigned char	ModeControl;	  /* CRT-Controller:17h */
551da177e4SLinus Torvalds 		unsigned char	ClockingMode;	  /* Seq-Controller:01h */
561da177e4SLinus Torvalds 	} vga_state;
571da177e4SLinus Torvalds 	struct vgastate state;
58c4f28e54SJiri Slaby 	unsigned int ref_count;
591da177e4SLinus Torvalds 	int palette_blanked, vesa_blanked, mode, isVGA;
601da177e4SLinus Torvalds 	u8 misc, pel_msk, vss, clkdiv;
611da177e4SLinus Torvalds 	u8 crtc[VGA_CRT_C];
62120ddb41SAntonino A. Daplas };
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds /* --------------------------------------------------------------------- */
651da177e4SLinus Torvalds 
6648c68c4fSGreg Kroah-Hartman static struct fb_var_screeninfo vga16fb_defined = {
671da177e4SLinus Torvalds 	.xres		= 640,
681da177e4SLinus Torvalds 	.yres		= 480,
691da177e4SLinus Torvalds 	.xres_virtual	= 640,
701da177e4SLinus Torvalds 	.yres_virtual	= 480,
711da177e4SLinus Torvalds 	.bits_per_pixel	= 4,
721da177e4SLinus Torvalds 	.activate	= FB_ACTIVATE_TEST,
731da177e4SLinus Torvalds 	.height		= -1,
741da177e4SLinus Torvalds 	.width		= -1,
751da177e4SLinus Torvalds 	.pixclock	= 39721,
761da177e4SLinus Torvalds 	.left_margin	= 48,
771da177e4SLinus Torvalds 	.right_margin	= 16,
781da177e4SLinus Torvalds 	.upper_margin	= 33,
791da177e4SLinus Torvalds 	.lower_margin	= 10,
801da177e4SLinus Torvalds 	.hsync_len 	= 96,
811da177e4SLinus Torvalds 	.vsync_len	= 2,
821da177e4SLinus Torvalds 	.vmode		= FB_VMODE_NONINTERLACED,
831da177e4SLinus Torvalds };
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds /* name should not depend on EGA/VGA */
86ca9384c5SJulia Lawall static const struct fb_fix_screeninfo vga16fb_fix = {
871da177e4SLinus Torvalds 	.id		= "VGA16 VGA",
884652905fSThomas Zimmermann 	.smem_start	= VGA_FB_PHYS_BASE,
894652905fSThomas Zimmermann 	.smem_len	= VGA_FB_PHYS_SIZE,
901da177e4SLinus Torvalds 	.type		= FB_TYPE_VGA_PLANES,
911da177e4SLinus Torvalds 	.type_aux	= FB_AUX_VGA_PLANES_VGA4,
921da177e4SLinus Torvalds 	.visual		= FB_VISUAL_PSEUDOCOLOR,
931da177e4SLinus Torvalds 	.xpanstep	= 8,
941da177e4SLinus Torvalds 	.ypanstep	= 1,
951da177e4SLinus Torvalds 	.line_length	= 640 / 8,
961da177e4SLinus Torvalds 	.accel		= FB_ACCEL_NONE
971da177e4SLinus Torvalds };
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds /* The VGA's weird architecture often requires that we read a byte and
1001da177e4SLinus Torvalds    write a byte to the same location.  It doesn't matter *what* byte
1011da177e4SLinus Torvalds    we write, however.  This is because all the action goes on behind
1021da177e4SLinus Torvalds    the scenes in the VGA's 32-bit latch register, and reading and writing
1031da177e4SLinus Torvalds    video memory just invokes latch behavior.
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds    To avoid race conditions (is this necessary?), reading and writing
1061da177e4SLinus Torvalds    the memory byte should be done with a single instruction.  One
1071da177e4SLinus Torvalds    suitable instruction is the x86 bitwise OR.  The following
1081da177e4SLinus Torvalds    read-modify-write routine should optimize to one such bitwise
1091da177e4SLinus Torvalds    OR. */
rmw(volatile char __iomem * p)1101da177e4SLinus Torvalds static inline void rmw(volatile char __iomem *p)
1111da177e4SLinus Torvalds {
1121da177e4SLinus Torvalds 	readb(p);
1131da177e4SLinus Torvalds 	writeb(1, p);
1141da177e4SLinus Torvalds }
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds /* Set the Graphics Mode Register, and return its previous value.
1171da177e4SLinus Torvalds    Bits 0-1 are write mode, bit 3 is read mode. */
setmode(int mode)1181da177e4SLinus Torvalds static inline int setmode(int mode)
1191da177e4SLinus Torvalds {
1201da177e4SLinus Torvalds 	int oldmode;
1211da177e4SLinus Torvalds 
12298219374SKrzysztof Helt 	oldmode = vga_io_rgfx(VGA_GFX_MODE);
12398219374SKrzysztof Helt 	vga_io_w(VGA_GFX_D, mode);
1241da177e4SLinus Torvalds 	return oldmode;
1251da177e4SLinus Torvalds }
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds /* Select the Bit Mask Register and return its value. */
selectmask(void)1281da177e4SLinus Torvalds static inline int selectmask(void)
1291da177e4SLinus Torvalds {
13098219374SKrzysztof Helt 	return vga_io_rgfx(VGA_GFX_BIT_MASK);
1311da177e4SLinus Torvalds }
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds /* Set the value of the Bit Mask Register.  It must already have been
1341da177e4SLinus Torvalds    selected with selectmask(). */
setmask(int mask)1351da177e4SLinus Torvalds static inline void setmask(int mask)
1361da177e4SLinus Torvalds {
13798219374SKrzysztof Helt 	vga_io_w(VGA_GFX_D, mask);
1381da177e4SLinus Torvalds }
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds /* Set the Data Rotate Register and return its old value.
1411da177e4SLinus Torvalds    Bits 0-2 are rotate count, bits 3-4 are logical operation
1421da177e4SLinus Torvalds    (0=NOP, 1=AND, 2=OR, 3=XOR). */
setop(int op)1431da177e4SLinus Torvalds static inline int setop(int op)
1441da177e4SLinus Torvalds {
1451da177e4SLinus Torvalds 	int oldop;
1461da177e4SLinus Torvalds 
14798219374SKrzysztof Helt 	oldop = vga_io_rgfx(VGA_GFX_DATA_ROTATE);
14898219374SKrzysztof Helt 	vga_io_w(VGA_GFX_D, op);
1491da177e4SLinus Torvalds 	return oldop;
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds /* Set the Enable Set/Reset Register and return its old value.
15325985edcSLucas De Marchi    The code here always uses value 0xf for this register. */
setsr(int sr)1541da177e4SLinus Torvalds static inline int setsr(int sr)
1551da177e4SLinus Torvalds {
1561da177e4SLinus Torvalds 	int oldsr;
1571da177e4SLinus Torvalds 
15898219374SKrzysztof Helt 	oldsr = vga_io_rgfx(VGA_GFX_SR_ENABLE);
15998219374SKrzysztof Helt 	vga_io_w(VGA_GFX_D, sr);
1601da177e4SLinus Torvalds 	return oldsr;
1611da177e4SLinus Torvalds }
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds /* Set the Set/Reset Register and return its old value. */
setcolor(int color)1641da177e4SLinus Torvalds static inline int setcolor(int color)
1651da177e4SLinus Torvalds {
1661da177e4SLinus Torvalds 	int oldcolor;
1671da177e4SLinus Torvalds 
16898219374SKrzysztof Helt 	oldcolor = vga_io_rgfx(VGA_GFX_SR_VALUE);
16998219374SKrzysztof Helt 	vga_io_w(VGA_GFX_D, color);
1701da177e4SLinus Torvalds 	return oldcolor;
1711da177e4SLinus Torvalds }
1721da177e4SLinus Torvalds 
1731da177e4SLinus Torvalds /* Return the value in the Graphics Address Register. */
getindex(void)1741da177e4SLinus Torvalds static inline int getindex(void)
1751da177e4SLinus Torvalds {
17698219374SKrzysztof Helt 	return vga_io_r(VGA_GFX_I);
1771da177e4SLinus Torvalds }
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds /* Set the value in the Graphics Address Register. */
setindex(int index)1801da177e4SLinus Torvalds static inline void setindex(int index)
1811da177e4SLinus Torvalds {
18298219374SKrzysztof Helt 	vga_io_w(VGA_GFX_I, index);
1831da177e4SLinus Torvalds }
1841da177e4SLinus Torvalds 
1850499f419SJavier Martinez Canillas /* Check if the video mode is supported by the driver */
check_mode_supported(const struct screen_info * si)1860db5b61eSThomas Zimmermann static inline int check_mode_supported(const struct screen_info *si)
1870499f419SJavier Martinez Canillas {
188*c542a56aSZsolt Kajtar 	unsigned int type = screen_info_video_type(si);
189*c542a56aSZsolt Kajtar 
1900499f419SJavier Martinez Canillas 	/* only EGA and VGA in 16 color graphic mode are supported */
191*c542a56aSZsolt Kajtar 	if (type != VIDEO_TYPE_EGAC && type != VIDEO_TYPE_VGAC)
1920499f419SJavier Martinez Canillas 		return -ENODEV;
1930499f419SJavier Martinez Canillas 
1940db5b61eSThomas Zimmermann 	if (si->orig_video_mode != 0x0D &&	/* 320x200/4 (EGA) */
1950db5b61eSThomas Zimmermann 	    si->orig_video_mode != 0x0E &&	/* 640x200/4 (EGA) */
1960db5b61eSThomas Zimmermann 	    si->orig_video_mode != 0x10 &&	/* 640x350/4 (EGA) */
1970db5b61eSThomas Zimmermann 	    si->orig_video_mode != 0x12)	/* 640x480/4 (VGA) */
1980499f419SJavier Martinez Canillas 		return -ENODEV;
199b858a97bSArnd Bergmann 
2000499f419SJavier Martinez Canillas 	return 0;
2010499f419SJavier Martinez Canillas }
2020499f419SJavier Martinez Canillas 
vga16fb_pan_var(struct fb_info * info,struct fb_var_screeninfo * var)2031da177e4SLinus Torvalds static void vga16fb_pan_var(struct fb_info *info,
2041da177e4SLinus Torvalds 			    struct fb_var_screeninfo *var)
2051da177e4SLinus Torvalds {
206120ddb41SAntonino A. Daplas 	struct vga16fb_par *par = info->par;
2071da177e4SLinus Torvalds 	u32 xoffset, pos;
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds 	xoffset = var->xoffset;
2101da177e4SLinus Torvalds 	if (info->var.bits_per_pixel == 8) {
2111da177e4SLinus Torvalds 		pos = (info->var.xres_virtual * var->yoffset + xoffset) >> 2;
2121da177e4SLinus Torvalds 	} else if (par->mode & MODE_TEXT) {
2131da177e4SLinus Torvalds 		int fh = 16; // FIXME !!! font height. Fugde for now.
2141da177e4SLinus Torvalds 		pos = (info->var.xres_virtual * (var->yoffset / fh) + xoffset) >> 3;
2151da177e4SLinus Torvalds 	} else {
2161da177e4SLinus Torvalds 		if (info->var.nonstd)
2171da177e4SLinus Torvalds 			xoffset--;
2181da177e4SLinus Torvalds 		pos = (info->var.xres_virtual * var->yoffset + xoffset) >> 3;
2191da177e4SLinus Torvalds 	}
2201da177e4SLinus Torvalds 	vga_io_wcrt(VGA_CRTC_START_HI, pos >> 8);
2211da177e4SLinus Torvalds 	vga_io_wcrt(VGA_CRTC_START_LO, pos & 0xFF);
2221da177e4SLinus Torvalds 	/* if we support CFB4, then we must! support xoffset with pixel
2231da177e4SLinus Torvalds 	 * granularity if someone supports xoffset in bit resolution */
2241da177e4SLinus Torvalds 	vga_io_r(VGA_IS1_RC);		/* reset flip-flop */
2251da177e4SLinus Torvalds 	vga_io_w(VGA_ATT_IW, VGA_ATC_PEL);
226c272d641SLaurent Pinchart 	if (info->var.bits_per_pixel == 8)
2271da177e4SLinus Torvalds 		vga_io_w(VGA_ATT_IW, (xoffset & 3) << 1);
2281da177e4SLinus Torvalds 	else
2291da177e4SLinus Torvalds 		vga_io_w(VGA_ATT_IW, xoffset & 7);
2301da177e4SLinus Torvalds 	vga_io_r(VGA_IS1_RC);
2311da177e4SLinus Torvalds 	vga_io_w(VGA_ATT_IW, 0x20);
2321da177e4SLinus Torvalds }
2331da177e4SLinus Torvalds 
vga16fb_update_fix(struct fb_info * info)2341da177e4SLinus Torvalds static void vga16fb_update_fix(struct fb_info *info)
2351da177e4SLinus Torvalds {
2361da177e4SLinus Torvalds 	if (info->var.bits_per_pixel == 4) {
2371da177e4SLinus Torvalds 		if (info->var.nonstd) {
2381da177e4SLinus Torvalds 			info->fix.type = FB_TYPE_PACKED_PIXELS;
2391da177e4SLinus Torvalds 			info->fix.line_length = info->var.xres_virtual / 2;
2401da177e4SLinus Torvalds 		} else {
2411da177e4SLinus Torvalds 			info->fix.type = FB_TYPE_VGA_PLANES;
2421da177e4SLinus Torvalds 			info->fix.type_aux = FB_AUX_VGA_PLANES_VGA4;
2431da177e4SLinus Torvalds 			info->fix.line_length = info->var.xres_virtual / 8;
2441da177e4SLinus Torvalds 		}
2451da177e4SLinus Torvalds 	} else if (info->var.bits_per_pixel == 0) {
2461da177e4SLinus Torvalds 		info->fix.type = FB_TYPE_TEXT;
2471da177e4SLinus Torvalds 		info->fix.type_aux = FB_AUX_TEXT_CGA;
2481da177e4SLinus Torvalds 		info->fix.line_length = info->var.xres_virtual / 4;
2491da177e4SLinus Torvalds 	} else {	/* 8bpp */
2501da177e4SLinus Torvalds 		if (info->var.nonstd) {
2511da177e4SLinus Torvalds 			info->fix.type = FB_TYPE_VGA_PLANES;
2521da177e4SLinus Torvalds 			info->fix.type_aux = FB_AUX_VGA_PLANES_CFB8;
2531da177e4SLinus Torvalds 			info->fix.line_length = info->var.xres_virtual / 4;
2541da177e4SLinus Torvalds 		} else {
2551da177e4SLinus Torvalds 			info->fix.type = FB_TYPE_PACKED_PIXELS;
2561da177e4SLinus Torvalds 			info->fix.line_length = info->var.xres_virtual;
2571da177e4SLinus Torvalds 		}
2581da177e4SLinus Torvalds 	}
2591da177e4SLinus Torvalds }
2601da177e4SLinus Torvalds 
vga16fb_clock_chip(struct vga16fb_par * par,unsigned int * pixclock,const struct fb_info * info,int mul,int div)2611da177e4SLinus Torvalds static void vga16fb_clock_chip(struct vga16fb_par *par,
262c72fab81SColin Ian King 			       unsigned int *pixclock,
2631da177e4SLinus Torvalds 			       const struct fb_info *info,
2641da177e4SLinus Torvalds 			       int mul, int div)
2651da177e4SLinus Torvalds {
266ec1a7b3dSHelge Deller 	static const struct {
2671da177e4SLinus Torvalds 		u32 pixclock;
2681da177e4SLinus Torvalds 		u8  misc;
2691da177e4SLinus Torvalds 		u8  seq_clock_mode;
2701da177e4SLinus Torvalds 	} *ptr, *best, vgaclocks[] = {
2711da177e4SLinus Torvalds 		{ 79442 /* 12.587 */, 0x00, 0x08},
2721da177e4SLinus Torvalds 		{ 70616 /* 14.161 */, 0x04, 0x08},
2731da177e4SLinus Torvalds 		{ 39721 /* 25.175 */, 0x00, 0x00},
2741da177e4SLinus Torvalds 		{ 35308 /* 28.322 */, 0x04, 0x00},
2751da177e4SLinus Torvalds 		{     0 /* bad */,    0x00, 0x00}};
2761da177e4SLinus Torvalds 	int err;
2771da177e4SLinus Torvalds 
278c72fab81SColin Ian King 	*pixclock = (*pixclock * mul) / div;
2791da177e4SLinus Torvalds 	best = vgaclocks;
280c72fab81SColin Ian King 	err = *pixclock - best->pixclock;
2811da177e4SLinus Torvalds 	if (err < 0) err = -err;
2821da177e4SLinus Torvalds 	for (ptr = vgaclocks + 1; ptr->pixclock; ptr++) {
2831da177e4SLinus Torvalds 		int tmp;
2841da177e4SLinus Torvalds 
285c72fab81SColin Ian King 		tmp = *pixclock - ptr->pixclock;
2861da177e4SLinus Torvalds 		if (tmp < 0) tmp = -tmp;
2871da177e4SLinus Torvalds 		if (tmp < err) {
2881da177e4SLinus Torvalds 			err = tmp;
2891da177e4SLinus Torvalds 			best = ptr;
2901da177e4SLinus Torvalds 		}
2911da177e4SLinus Torvalds 	}
2921da177e4SLinus Torvalds 	par->misc |= best->misc;
2931da177e4SLinus Torvalds 	par->clkdiv = best->seq_clock_mode;
294c72fab81SColin Ian King 	*pixclock = (best->pixclock * div) / mul;
2951da177e4SLinus Torvalds }
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds #define FAIL(X) return -EINVAL
2981da177e4SLinus Torvalds 
vga16fb_open(struct fb_info * info,int user)2991da177e4SLinus Torvalds static int vga16fb_open(struct fb_info *info, int user)
3001da177e4SLinus Torvalds {
301120ddb41SAntonino A. Daplas 	struct vga16fb_par *par = info->par;
3021da177e4SLinus Torvalds 
303c4f28e54SJiri Slaby 	if (!par->ref_count) {
3041da177e4SLinus Torvalds 		memset(&par->state, 0, sizeof(struct vgastate));
3051da177e4SLinus Torvalds 		par->state.flags = VGA_SAVE_FONTS | VGA_SAVE_MODE |
3061da177e4SLinus Torvalds 			VGA_SAVE_CMAP;
3071da177e4SLinus Torvalds 		save_vga(&par->state);
3081da177e4SLinus Torvalds 	}
309c4f28e54SJiri Slaby 	par->ref_count++;
310c4f28e54SJiri Slaby 
3111da177e4SLinus Torvalds 	return 0;
3121da177e4SLinus Torvalds }
3131da177e4SLinus Torvalds 
vga16fb_release(struct fb_info * info,int user)3141da177e4SLinus Torvalds static int vga16fb_release(struct fb_info *info, int user)
3151da177e4SLinus Torvalds {
316120ddb41SAntonino A. Daplas 	struct vga16fb_par *par = info->par;
3171da177e4SLinus Torvalds 
3189c8db4a2SKrzysztof Helt 	if (!par->ref_count)
3191da177e4SLinus Torvalds 		return -EINVAL;
3209c8db4a2SKrzysztof Helt 
321c4f28e54SJiri Slaby 	if (par->ref_count == 1)
3221da177e4SLinus Torvalds 		restore_vga(&par->state);
323c4f28e54SJiri Slaby 	par->ref_count--;
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 	return 0;
3261da177e4SLinus Torvalds }
3271da177e4SLinus Torvalds 
vga16fb_check_var(struct fb_var_screeninfo * var,struct fb_info * info)3281da177e4SLinus Torvalds static int vga16fb_check_var(struct fb_var_screeninfo *var,
3291da177e4SLinus Torvalds 			     struct fb_info *info)
3301da177e4SLinus Torvalds {
331120ddb41SAntonino A. Daplas 	struct vga16fb_par *par = info->par;
3321da177e4SLinus Torvalds 	u32 xres, right, hslen, left, xtotal;
3331da177e4SLinus Torvalds 	u32 yres, lower, vslen, upper, ytotal;
3341da177e4SLinus Torvalds 	u32 vxres, xoffset, vyres, yoffset;
3351da177e4SLinus Torvalds 	u32 pos;
3361da177e4SLinus Torvalds 	u8 r7, rMode;
3371da177e4SLinus Torvalds 	int shift;
3381da177e4SLinus Torvalds 	int mode;
3391da177e4SLinus Torvalds 	u32 maxmem;
3401da177e4SLinus Torvalds 
3411da177e4SLinus Torvalds 	par->pel_msk = 0xFF;
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds 	if (var->bits_per_pixel == 4) {
3441da177e4SLinus Torvalds 		if (var->nonstd) {
3451da177e4SLinus Torvalds 			if (!par->isVGA)
3461da177e4SLinus Torvalds 				return -EINVAL;
3471da177e4SLinus Torvalds 			shift = 3;
3481da177e4SLinus Torvalds 			mode = MODE_SKIP4 | MODE_CFB;
3491da177e4SLinus Torvalds 			maxmem = 16384;
3501da177e4SLinus Torvalds 			par->pel_msk = 0x0F;
3511da177e4SLinus Torvalds 		} else {
3521da177e4SLinus Torvalds 			shift = 3;
3531da177e4SLinus Torvalds 			mode = 0;
3541da177e4SLinus Torvalds 			maxmem = 65536;
3551da177e4SLinus Torvalds 		}
3561da177e4SLinus Torvalds 	} else if (var->bits_per_pixel == 8) {
3571da177e4SLinus Torvalds 		if (!par->isVGA)
3581da177e4SLinus Torvalds 			return -EINVAL;	/* no support on EGA */
3591da177e4SLinus Torvalds 		shift = 2;
3601da177e4SLinus Torvalds 		if (var->nonstd) {
3611da177e4SLinus Torvalds 			mode = MODE_8BPP | MODE_CFB;
3621da177e4SLinus Torvalds 			maxmem = 65536;
3631da177e4SLinus Torvalds 		} else {
3641da177e4SLinus Torvalds 			mode = MODE_SKIP4 | MODE_8BPP | MODE_CFB;
3651da177e4SLinus Torvalds 			maxmem = 16384;
3661da177e4SLinus Torvalds 		}
3671da177e4SLinus Torvalds 	} else
3681da177e4SLinus Torvalds 		return -EINVAL;
3691da177e4SLinus Torvalds 
3701da177e4SLinus Torvalds 	xres = (var->xres + 7) & ~7;
3711da177e4SLinus Torvalds 	vxres = (var->xres_virtual + 0xF) & ~0xF;
3721da177e4SLinus Torvalds 	xoffset = (var->xoffset + 7) & ~7;
3731da177e4SLinus Torvalds 	left = (var->left_margin + 7) & ~7;
3741da177e4SLinus Torvalds 	right = (var->right_margin + 7) & ~7;
3751da177e4SLinus Torvalds 	hslen = (var->hsync_len + 7) & ~7;
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds 	if (vxres < xres)
3781da177e4SLinus Torvalds 		vxres = xres;
3791da177e4SLinus Torvalds 	if (xres + xoffset > vxres)
3801da177e4SLinus Torvalds 		xoffset = vxres - xres;
3811da177e4SLinus Torvalds 
3821da177e4SLinus Torvalds 	var->xres = xres;
3831da177e4SLinus Torvalds 	var->right_margin = right;
3841da177e4SLinus Torvalds 	var->hsync_len = hslen;
3851da177e4SLinus Torvalds 	var->left_margin = left;
3861da177e4SLinus Torvalds 	var->xres_virtual = vxres;
3871da177e4SLinus Torvalds 	var->xoffset = xoffset;
3881da177e4SLinus Torvalds 
3891da177e4SLinus Torvalds 	xres >>= shift;
3901da177e4SLinus Torvalds 	right >>= shift;
3911da177e4SLinus Torvalds 	hslen >>= shift;
3921da177e4SLinus Torvalds 	left >>= shift;
3931da177e4SLinus Torvalds 	vxres >>= shift;
3941da177e4SLinus Torvalds 	xtotal = xres + right + hslen + left;
3951da177e4SLinus Torvalds 	if (xtotal >= 256)
3961da177e4SLinus Torvalds 		FAIL("xtotal too big");
3971da177e4SLinus Torvalds 	if (hslen > 32)
3981da177e4SLinus Torvalds 		FAIL("hslen too big");
3991da177e4SLinus Torvalds 	if (right + hslen + left > 64)
4001da177e4SLinus Torvalds 		FAIL("hblank too big");
4011da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_H_TOTAL] = xtotal - 5;
4021da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_H_BLANK_START] = xres - 1;
4031da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_H_DISP] = xres - 1;
4041da177e4SLinus Torvalds 	pos = xres + right;
4051da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_H_SYNC_START] = pos;
4061da177e4SLinus Torvalds 	pos += hslen;
4071da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_H_SYNC_END] = pos & 0x1F;
4081da177e4SLinus Torvalds 	pos += left - 2; /* blank_end + 2 <= total + 5 */
4091da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_H_BLANK_END] = (pos & 0x1F) | 0x80;
4101da177e4SLinus Torvalds 	if (pos & 0x20)
4111da177e4SLinus Torvalds 		par->crtc[VGA_CRTC_H_SYNC_END] |= 0x80;
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds 	yres = var->yres;
4141da177e4SLinus Torvalds 	lower = var->lower_margin;
4151da177e4SLinus Torvalds 	vslen = var->vsync_len;
4161da177e4SLinus Torvalds 	upper = var->upper_margin;
4171da177e4SLinus Torvalds 	vyres = var->yres_virtual;
4181da177e4SLinus Torvalds 	yoffset = var->yoffset;
4191da177e4SLinus Torvalds 
4201da177e4SLinus Torvalds 	if (yres > vyres)
4211da177e4SLinus Torvalds 		vyres = yres;
4221da177e4SLinus Torvalds 	if (vxres * vyres > maxmem) {
4231da177e4SLinus Torvalds 		vyres = maxmem / vxres;
4241da177e4SLinus Torvalds 		if (vyres < yres)
4251da177e4SLinus Torvalds 			return -ENOMEM;
4261da177e4SLinus Torvalds 	}
4271da177e4SLinus Torvalds 	if (yoffset + yres > vyres)
4281da177e4SLinus Torvalds 		yoffset = vyres - yres;
4291da177e4SLinus Torvalds 	var->yres = yres;
4301da177e4SLinus Torvalds 	var->lower_margin = lower;
4311da177e4SLinus Torvalds 	var->vsync_len = vslen;
4321da177e4SLinus Torvalds 	var->upper_margin = upper;
4331da177e4SLinus Torvalds 	var->yres_virtual = vyres;
4341da177e4SLinus Torvalds 	var->yoffset = yoffset;
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 	if (var->vmode & FB_VMODE_DOUBLE) {
4371da177e4SLinus Torvalds 		yres <<= 1;
4381da177e4SLinus Torvalds 		lower <<= 1;
4391da177e4SLinus Torvalds 		vslen <<= 1;
4401da177e4SLinus Torvalds 		upper <<= 1;
4411da177e4SLinus Torvalds 	}
4421da177e4SLinus Torvalds 	ytotal = yres + lower + vslen + upper;
4431da177e4SLinus Torvalds 	if (ytotal > 1024) {
4441da177e4SLinus Torvalds 		ytotal >>= 1;
4451da177e4SLinus Torvalds 		yres >>= 1;
4461da177e4SLinus Torvalds 		lower >>= 1;
4471da177e4SLinus Torvalds 		vslen >>= 1;
4481da177e4SLinus Torvalds 		upper >>= 1;
4491da177e4SLinus Torvalds 		rMode = 0x04;
4501da177e4SLinus Torvalds 	} else
4511da177e4SLinus Torvalds 		rMode = 0x00;
4521da177e4SLinus Torvalds 	if (ytotal > 1024)
4531da177e4SLinus Torvalds 		FAIL("ytotal too big");
4541da177e4SLinus Torvalds 	if (vslen > 16)
4551da177e4SLinus Torvalds 		FAIL("vslen too big");
4561da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_V_TOTAL] = ytotal - 2;
4571da177e4SLinus Torvalds 	r7 = 0x10;	/* disable linecompare */
4581da177e4SLinus Torvalds 	if (ytotal & 0x100) r7 |= 0x01;
4591da177e4SLinus Torvalds 	if (ytotal & 0x200) r7 |= 0x20;
4601da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_PRESET_ROW] = 0;
4611da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_MAX_SCAN] = 0x40;	/* 1 scanline, no linecmp */
4621da177e4SLinus Torvalds 	if (var->vmode & FB_VMODE_DOUBLE)
4631da177e4SLinus Torvalds 		par->crtc[VGA_CRTC_MAX_SCAN] |= 0x80;
4641da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_CURSOR_START] = 0x20;
4651da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_CURSOR_END]   = 0x00;
4661da177e4SLinus Torvalds 	if ((mode & (MODE_CFB | MODE_8BPP)) == MODE_CFB)
4671da177e4SLinus Torvalds 		xoffset--;
4681da177e4SLinus Torvalds 	pos = yoffset * vxres + (xoffset >> shift);
4691da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_START_HI]     = pos >> 8;
4701da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_START_LO]     = pos & 0xFF;
4711da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_CURSOR_HI]    = 0x00;
4721da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_CURSOR_LO]    = 0x00;
4731da177e4SLinus Torvalds 	pos = yres - 1;
4741da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_V_DISP_END] = pos & 0xFF;
4751da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_V_BLANK_START] = pos & 0xFF;
4761da177e4SLinus Torvalds 	if (pos & 0x100)
4771da177e4SLinus Torvalds 		r7 |= 0x0A;	/* 0x02 -> DISP_END, 0x08 -> BLANK_START */
4781da177e4SLinus Torvalds 	if (pos & 0x200) {
4791da177e4SLinus Torvalds 		r7 |= 0x40;	/* 0x40 -> DISP_END */
4801da177e4SLinus Torvalds 		par->crtc[VGA_CRTC_MAX_SCAN] |= 0x20; /* BLANK_START */
4811da177e4SLinus Torvalds 	}
4821da177e4SLinus Torvalds 	pos += lower;
4831da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_V_SYNC_START] = pos & 0xFF;
4841da177e4SLinus Torvalds 	if (pos & 0x100)
4851da177e4SLinus Torvalds 		r7 |= 0x04;
4861da177e4SLinus Torvalds 	if (pos & 0x200)
4871da177e4SLinus Torvalds 		r7 |= 0x80;
4881da177e4SLinus Torvalds 	pos += vslen;
4891da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_V_SYNC_END] = (pos & 0x0F) & ~0x10; /* disabled IRQ */
4901da177e4SLinus Torvalds 	pos += upper - 1; /* blank_end + 1 <= ytotal + 2 */
4911da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_V_BLANK_END] = pos & 0xFF; /* 0x7F for original VGA,
4921da177e4SLinus Torvalds                      but some SVGA chips requires all 8 bits to set */
4931da177e4SLinus Torvalds 	if (vxres >= 512)
4941da177e4SLinus Torvalds 		FAIL("vxres too long");
4951da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_OFFSET] = vxres >> 1;
4961da177e4SLinus Torvalds 	if (mode & MODE_SKIP4)
4971da177e4SLinus Torvalds 		par->crtc[VGA_CRTC_UNDERLINE] = 0x5F;	/* 256, cfb8 */
4981da177e4SLinus Torvalds 	else
4991da177e4SLinus Torvalds 		par->crtc[VGA_CRTC_UNDERLINE] = 0x1F;	/* 16, vgap */
5001da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_MODE] = rMode | ((mode & MODE_TEXT) ? 0xA3 : 0xE3);
5011da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_LINE_COMPARE] = 0xFF;
5021da177e4SLinus Torvalds 	par->crtc[VGA_CRTC_OVERFLOW] = r7;
5031da177e4SLinus Torvalds 
5041da177e4SLinus Torvalds 	par->vss = 0x00;	/* 3DA */
5051da177e4SLinus Torvalds 
5061da177e4SLinus Torvalds 	par->misc = 0xE3;	/* enable CPU, ports 0x3Dx, positive sync */
5071da177e4SLinus Torvalds 	if (var->sync & FB_SYNC_HOR_HIGH_ACT)
5081da177e4SLinus Torvalds 		par->misc &= ~0x40;
5091da177e4SLinus Torvalds 	if (var->sync & FB_SYNC_VERT_HIGH_ACT)
5101da177e4SLinus Torvalds 		par->misc &= ~0x80;
5111da177e4SLinus Torvalds 
5121da177e4SLinus Torvalds 	par->mode = mode;
5131da177e4SLinus Torvalds 
5141da177e4SLinus Torvalds 	if (mode & MODE_8BPP)
5151da177e4SLinus Torvalds 		/* pixel clock == vga clock / 2 */
516c72fab81SColin Ian King 		vga16fb_clock_chip(par, &var->pixclock, info, 1, 2);
5171da177e4SLinus Torvalds 	else
5181da177e4SLinus Torvalds 		/* pixel clock == vga clock */
519c72fab81SColin Ian King 		vga16fb_clock_chip(par, &var->pixclock, info, 1, 1);
5201da177e4SLinus Torvalds 
5211da177e4SLinus Torvalds 	var->red.offset = var->green.offset = var->blue.offset =
5221da177e4SLinus Torvalds 	var->transp.offset = 0;
5231da177e4SLinus Torvalds 	var->red.length = var->green.length = var->blue.length =
5241da177e4SLinus Torvalds 		(par->isVGA) ? 6 : 2;
5251da177e4SLinus Torvalds 	var->transp.length = 0;
5261da177e4SLinus Torvalds 	var->activate = FB_ACTIVATE_NOW;
5271da177e4SLinus Torvalds 	var->height = -1;
5281da177e4SLinus Torvalds 	var->width = -1;
5291da177e4SLinus Torvalds 	var->accel_flags = 0;
5301da177e4SLinus Torvalds 	return 0;
5311da177e4SLinus Torvalds }
5321da177e4SLinus Torvalds #undef FAIL
5331da177e4SLinus Torvalds 
vga16fb_set_par(struct fb_info * info)5341da177e4SLinus Torvalds static int vga16fb_set_par(struct fb_info *info)
5351da177e4SLinus Torvalds {
536120ddb41SAntonino A. Daplas 	struct vga16fb_par *par = info->par;
5371da177e4SLinus Torvalds 	u8 gdc[VGA_GFX_C];
5381da177e4SLinus Torvalds 	u8 seq[VGA_SEQ_C];
5391da177e4SLinus Torvalds 	u8 atc[VGA_ATT_C];
5401da177e4SLinus Torvalds 	int fh, i;
5411da177e4SLinus Torvalds 
5421da177e4SLinus Torvalds 	seq[VGA_SEQ_CLOCK_MODE] = 0x01 | par->clkdiv;
5431da177e4SLinus Torvalds 	if (par->mode & MODE_TEXT)
5441da177e4SLinus Torvalds 		seq[VGA_SEQ_PLANE_WRITE] = 0x03;
5451da177e4SLinus Torvalds 	else
5461da177e4SLinus Torvalds 		seq[VGA_SEQ_PLANE_WRITE] = 0x0F;
5471da177e4SLinus Torvalds 	seq[VGA_SEQ_CHARACTER_MAP] = 0x00;
5481da177e4SLinus Torvalds 	if (par->mode & MODE_TEXT)
5491da177e4SLinus Torvalds 		seq[VGA_SEQ_MEMORY_MODE] = 0x03;
5501da177e4SLinus Torvalds 	else if (par->mode & MODE_SKIP4)
5511da177e4SLinus Torvalds 		seq[VGA_SEQ_MEMORY_MODE] = 0x0E;
5521da177e4SLinus Torvalds 	else
5531da177e4SLinus Torvalds 		seq[VGA_SEQ_MEMORY_MODE] = 0x06;
5541da177e4SLinus Torvalds 
5551da177e4SLinus Torvalds 	gdc[VGA_GFX_SR_VALUE] = 0x00;
5561da177e4SLinus Torvalds 	gdc[VGA_GFX_SR_ENABLE] = 0x00;
5571da177e4SLinus Torvalds 	gdc[VGA_GFX_COMPARE_VALUE] = 0x00;
5581da177e4SLinus Torvalds 	gdc[VGA_GFX_DATA_ROTATE] = 0x00;
5591da177e4SLinus Torvalds 	gdc[VGA_GFX_PLANE_READ] = 0;
5601da177e4SLinus Torvalds 	if (par->mode & MODE_TEXT) {
5611da177e4SLinus Torvalds 		gdc[VGA_GFX_MODE] = 0x10;
5621da177e4SLinus Torvalds 		gdc[VGA_GFX_MISC] = 0x06;
5631da177e4SLinus Torvalds 	} else {
5641da177e4SLinus Torvalds 		if (par->mode & MODE_CFB)
5651da177e4SLinus Torvalds 			gdc[VGA_GFX_MODE] = 0x40;
5661da177e4SLinus Torvalds 		else
5671da177e4SLinus Torvalds 			gdc[VGA_GFX_MODE] = 0x00;
5681da177e4SLinus Torvalds 		gdc[VGA_GFX_MISC] = 0x05;
5691da177e4SLinus Torvalds 	}
5701da177e4SLinus Torvalds 	gdc[VGA_GFX_COMPARE_MASK] = 0x0F;
5711da177e4SLinus Torvalds 	gdc[VGA_GFX_BIT_MASK] = 0xFF;
5721da177e4SLinus Torvalds 
5731da177e4SLinus Torvalds 	for (i = 0x00; i < 0x10; i++)
5741da177e4SLinus Torvalds 		atc[i] = i;
5751da177e4SLinus Torvalds 	if (par->mode & MODE_TEXT)
5761da177e4SLinus Torvalds 		atc[VGA_ATC_MODE] = 0x04;
5771da177e4SLinus Torvalds 	else if (par->mode & MODE_8BPP)
5781da177e4SLinus Torvalds 		atc[VGA_ATC_MODE] = 0x41;
5791da177e4SLinus Torvalds 	else
5801da177e4SLinus Torvalds 		atc[VGA_ATC_MODE] = 0x81;
5811da177e4SLinus Torvalds 	atc[VGA_ATC_OVERSCAN] = 0x00;	/* 0 for EGA, 0xFF for VGA */
5821da177e4SLinus Torvalds 	atc[VGA_ATC_PLANE_ENABLE] = 0x0F;
5831da177e4SLinus Torvalds 	if (par->mode & MODE_8BPP)
5841da177e4SLinus Torvalds 		atc[VGA_ATC_PEL] = (info->var.xoffset & 3) << 1;
5851da177e4SLinus Torvalds 	else
5861da177e4SLinus Torvalds 		atc[VGA_ATC_PEL] = info->var.xoffset & 7;
5871da177e4SLinus Torvalds 	atc[VGA_ATC_COLOR_PAGE] = 0x00;
5881da177e4SLinus Torvalds 
5891da177e4SLinus Torvalds 	if (par->mode & MODE_TEXT) {
5901da177e4SLinus Torvalds 		fh = 16; // FIXME !!! Fudge font height.
5911da177e4SLinus Torvalds 		par->crtc[VGA_CRTC_MAX_SCAN] = (par->crtc[VGA_CRTC_MAX_SCAN]
5921da177e4SLinus Torvalds 					       & ~0x1F) | (fh - 1);
5931da177e4SLinus Torvalds 	}
5941da177e4SLinus Torvalds 
5951da177e4SLinus Torvalds 	vga_io_w(VGA_MIS_W, vga_io_r(VGA_MIS_R) | 0x01);
5961da177e4SLinus Torvalds 
5971da177e4SLinus Torvalds 	/* Enable graphics register modification */
5981da177e4SLinus Torvalds 	if (!par->isVGA) {
5991da177e4SLinus Torvalds 		vga_io_w(EGA_GFX_E0, 0x00);
6001da177e4SLinus Torvalds 		vga_io_w(EGA_GFX_E1, 0x01);
6011da177e4SLinus Torvalds 	}
6021da177e4SLinus Torvalds 
6031da177e4SLinus Torvalds 	/* update misc output register */
6041da177e4SLinus Torvalds 	vga_io_w(VGA_MIS_W, par->misc);
6051da177e4SLinus Torvalds 
6061da177e4SLinus Torvalds 	/* synchronous reset on */
6071da177e4SLinus Torvalds 	vga_io_wseq(0x00, 0x01);
6081da177e4SLinus Torvalds 
6091da177e4SLinus Torvalds 	if (par->isVGA)
6101da177e4SLinus Torvalds 		vga_io_w(VGA_PEL_MSK, par->pel_msk);
6111da177e4SLinus Torvalds 
6121da177e4SLinus Torvalds 	/* write sequencer registers */
6131da177e4SLinus Torvalds 	vga_io_wseq(VGA_SEQ_CLOCK_MODE, seq[VGA_SEQ_CLOCK_MODE] | 0x20);
6141da177e4SLinus Torvalds 	for (i = 2; i < VGA_SEQ_C; i++) {
6151da177e4SLinus Torvalds 		vga_io_wseq(i, seq[i]);
6161da177e4SLinus Torvalds 	}
6171da177e4SLinus Torvalds 
6181da177e4SLinus Torvalds 	/* synchronous reset off */
6191da177e4SLinus Torvalds 	vga_io_wseq(0x00, 0x03);
6201da177e4SLinus Torvalds 
6211da177e4SLinus Torvalds 	/* deprotect CRT registers 0-7 */
6221da177e4SLinus Torvalds 	vga_io_wcrt(VGA_CRTC_V_SYNC_END, par->crtc[VGA_CRTC_V_SYNC_END]);
6231da177e4SLinus Torvalds 
6241da177e4SLinus Torvalds 	/* write CRT registers */
6251da177e4SLinus Torvalds 	for (i = 0; i < VGA_CRTC_REGS; i++) {
6261da177e4SLinus Torvalds 		vga_io_wcrt(i, par->crtc[i]);
6271da177e4SLinus Torvalds 	}
6281da177e4SLinus Torvalds 
6291da177e4SLinus Torvalds 	/* write graphics controller registers */
6301da177e4SLinus Torvalds 	for (i = 0; i < VGA_GFX_C; i++) {
6311da177e4SLinus Torvalds 		vga_io_wgfx(i, gdc[i]);
6321da177e4SLinus Torvalds 	}
6331da177e4SLinus Torvalds 
6341da177e4SLinus Torvalds 	/* write attribute controller registers */
6351da177e4SLinus Torvalds 	for (i = 0; i < VGA_ATT_C; i++) {
6361da177e4SLinus Torvalds 		vga_io_r(VGA_IS1_RC);		/* reset flip-flop */
6371da177e4SLinus Torvalds 		vga_io_wattr(i, atc[i]);
6381da177e4SLinus Torvalds 	}
6391da177e4SLinus Torvalds 
6401da177e4SLinus Torvalds 	/* Wait for screen to stabilize. */
6411da177e4SLinus Torvalds 	mdelay(50);
6421da177e4SLinus Torvalds 
6431da177e4SLinus Torvalds 	vga_io_wseq(VGA_SEQ_CLOCK_MODE, seq[VGA_SEQ_CLOCK_MODE]);
6441da177e4SLinus Torvalds 
6451da177e4SLinus Torvalds 	vga_io_r(VGA_IS1_RC);
6461da177e4SLinus Torvalds 	vga_io_w(VGA_ATT_IW, 0x20);
6471da177e4SLinus Torvalds 
6481da177e4SLinus Torvalds 	vga16fb_update_fix(info);
6491da177e4SLinus Torvalds 	return 0;
6501da177e4SLinus Torvalds }
6511da177e4SLinus Torvalds 
ega16_setpalette(int regno,unsigned red,unsigned green,unsigned blue)6521da177e4SLinus Torvalds static void ega16_setpalette(int regno, unsigned red, unsigned green, unsigned blue)
6531da177e4SLinus Torvalds {
654ec1a7b3dSHelge Deller 	static const unsigned char map[] = { 000, 001, 010, 011 };
6551da177e4SLinus Torvalds 	int val;
6561da177e4SLinus Torvalds 
6571da177e4SLinus Torvalds 	if (regno >= 16)
6581da177e4SLinus Torvalds 		return;
6591da177e4SLinus Torvalds 	val = map[red>>14] | ((map[green>>14]) << 1) | ((map[blue>>14]) << 2);
6601da177e4SLinus Torvalds 	vga_io_r(VGA_IS1_RC);   /* ! 0x3BA */
6611da177e4SLinus Torvalds 	vga_io_wattr(regno, val);
6621da177e4SLinus Torvalds 	vga_io_r(VGA_IS1_RC);   /* some clones need it */
6631da177e4SLinus Torvalds 	vga_io_w(VGA_ATT_IW, 0x20); /* unblank screen */
6641da177e4SLinus Torvalds }
6651da177e4SLinus Torvalds 
vga16_setpalette(int regno,unsigned red,unsigned green,unsigned blue)6661da177e4SLinus Torvalds static void vga16_setpalette(int regno, unsigned red, unsigned green, unsigned blue)
6671da177e4SLinus Torvalds {
66898219374SKrzysztof Helt 	outb(regno,       VGA_PEL_IW);
66998219374SKrzysztof Helt 	outb(red   >> 10, VGA_PEL_D);
67098219374SKrzysztof Helt 	outb(green >> 10, VGA_PEL_D);
67198219374SKrzysztof Helt 	outb(blue  >> 10, VGA_PEL_D);
6721da177e4SLinus Torvalds }
6731da177e4SLinus Torvalds 
vga16fb_setcolreg(unsigned regno,unsigned red,unsigned green,unsigned blue,unsigned transp,struct fb_info * info)6741da177e4SLinus Torvalds static int vga16fb_setcolreg(unsigned regno, unsigned red, unsigned green,
6751da177e4SLinus Torvalds 			     unsigned blue, unsigned transp,
6761da177e4SLinus Torvalds 			     struct fb_info *info)
6771da177e4SLinus Torvalds {
678120ddb41SAntonino A. Daplas 	struct vga16fb_par *par = info->par;
6791da177e4SLinus Torvalds 	int gray;
6801da177e4SLinus Torvalds 
6811da177e4SLinus Torvalds 	/*
6821da177e4SLinus Torvalds 	 *  Set a single color register. The values supplied are
6831da177e4SLinus Torvalds 	 *  already rounded down to the hardware's capabilities
6841da177e4SLinus Torvalds 	 *  (according to the entries in the `var' structure). Return
6851da177e4SLinus Torvalds 	 *  != 0 for invalid regno.
6861da177e4SLinus Torvalds 	 */
6871da177e4SLinus Torvalds 
6881da177e4SLinus Torvalds 	if (regno >= 256)
6891da177e4SLinus Torvalds 		return 1;
6901da177e4SLinus Torvalds 
6911da177e4SLinus Torvalds 	gray = info->var.grayscale;
6921da177e4SLinus Torvalds 
6931da177e4SLinus Torvalds 	if (gray) {
6941da177e4SLinus Torvalds 		/* gray = 0.30*R + 0.59*G + 0.11*B */
6951da177e4SLinus Torvalds 		red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
6961da177e4SLinus Torvalds 	}
6971da177e4SLinus Torvalds 	if (par->isVGA)
6981da177e4SLinus Torvalds 		vga16_setpalette(regno,red,green,blue);
6991da177e4SLinus Torvalds 	else
7001da177e4SLinus Torvalds 		ega16_setpalette(regno,red,green,blue);
7011da177e4SLinus Torvalds 	return 0;
7021da177e4SLinus Torvalds }
7031da177e4SLinus Torvalds 
vga16fb_pan_display(struct fb_var_screeninfo * var,struct fb_info * info)7041da177e4SLinus Torvalds static int vga16fb_pan_display(struct fb_var_screeninfo *var,
7051da177e4SLinus Torvalds 			       struct fb_info *info)
7061da177e4SLinus Torvalds {
7071da177e4SLinus Torvalds 	vga16fb_pan_var(info, var);
7081da177e4SLinus Torvalds 	return 0;
7091da177e4SLinus Torvalds }
7101da177e4SLinus Torvalds 
7111da177e4SLinus Torvalds /* The following VESA blanking code is taken from vgacon.c.  The VGA
7121da177e4SLinus Torvalds    blanking code was originally by Huang shi chao, and modified by
7131da177e4SLinus Torvalds    Christoph Rimek (chrimek@toppoint.de) and todd j. derr
7141da177e4SLinus Torvalds    (tjd@barefoot.org) for Linux. */
7151da177e4SLinus Torvalds 
vga_vesa_blank(struct vga16fb_par * par,int mode)7161da177e4SLinus Torvalds static void vga_vesa_blank(struct vga16fb_par *par, int mode)
7171da177e4SLinus Torvalds {
71898219374SKrzysztof Helt 	unsigned char SeqCtrlIndex = vga_io_r(VGA_SEQ_I);
71998219374SKrzysztof Helt 	unsigned char CrtCtrlIndex = vga_io_r(VGA_CRT_IC);
7201da177e4SLinus Torvalds 
7211da177e4SLinus Torvalds 	/* save original values of VGA controller registers */
7221da177e4SLinus Torvalds 	if(!par->vesa_blanked) {
72398219374SKrzysztof Helt 		par->vga_state.CrtMiscIO = vga_io_r(VGA_MIS_R);
7241da177e4SLinus Torvalds 		//sti();
7251da177e4SLinus Torvalds 
7261da177e4SLinus Torvalds 		par->vga_state.HorizontalTotal = vga_io_rcrt(0x00);	/* HorizontalTotal */
7271da177e4SLinus Torvalds 		par->vga_state.HorizDisplayEnd = vga_io_rcrt(0x01);	/* HorizDisplayEnd */
7281da177e4SLinus Torvalds 		par->vga_state.StartHorizRetrace = vga_io_rcrt(0x04);	/* StartHorizRetrace */
7291da177e4SLinus Torvalds 		par->vga_state.EndHorizRetrace = vga_io_rcrt(0x05);	/* EndHorizRetrace */
7301da177e4SLinus Torvalds 		par->vga_state.Overflow = vga_io_rcrt(0x07);		/* Overflow */
7311da177e4SLinus Torvalds 		par->vga_state.StartVertRetrace = vga_io_rcrt(0x10);	/* StartVertRetrace */
7321da177e4SLinus Torvalds 		par->vga_state.EndVertRetrace = vga_io_rcrt(0x11);	/* EndVertRetrace */
7331da177e4SLinus Torvalds 		par->vga_state.ModeControl = vga_io_rcrt(0x17);	/* ModeControl */
7341da177e4SLinus Torvalds 		par->vga_state.ClockingMode = vga_io_rseq(0x01);	/* ClockingMode */
7351da177e4SLinus Torvalds 	}
7361da177e4SLinus Torvalds 
7371da177e4SLinus Torvalds 	/* assure that video is enabled */
7381da177e4SLinus Torvalds 	/* "0x20" is VIDEO_ENABLE_bit in register 01 of sequencer */
7391da177e4SLinus Torvalds 	vga_io_wseq(0x01, par->vga_state.ClockingMode | 0x20);
7401da177e4SLinus Torvalds 
7411da177e4SLinus Torvalds 	/* test for vertical retrace in process.... */
7421da177e4SLinus Torvalds 	if ((par->vga_state.CrtMiscIO & 0x80) == 0x80)
74398219374SKrzysztof Helt 		vga_io_w(VGA_MIS_W, par->vga_state.CrtMiscIO & 0xef);
7441da177e4SLinus Torvalds 
7451da177e4SLinus Torvalds 	/*
7461da177e4SLinus Torvalds 	 * Set <End of vertical retrace> to minimum (0) and
7471da177e4SLinus Torvalds 	 * <Start of vertical Retrace> to maximum (incl. overflow)
7481da177e4SLinus Torvalds 	 * Result: turn off vertical sync (VSync) pulse.
7491da177e4SLinus Torvalds 	 */
7501da177e4SLinus Torvalds 	if (mode & FB_BLANK_VSYNC_SUSPEND) {
75198219374SKrzysztof Helt 		vga_io_wcrt(VGA_CRTC_V_SYNC_START, 0xff);
75298219374SKrzysztof Helt 		vga_io_wcrt(VGA_CRTC_V_SYNC_END, 0x40);
75398219374SKrzysztof Helt 		/* bits 9,10 of vert. retrace */
75498219374SKrzysztof Helt 		vga_io_wcrt(VGA_CRTC_OVERFLOW, par->vga_state.Overflow | 0x84);
7551da177e4SLinus Torvalds 	}
7561da177e4SLinus Torvalds 
7571da177e4SLinus Torvalds 	if (mode & FB_BLANK_HSYNC_SUSPEND) {
7581da177e4SLinus Torvalds 		/*
7591da177e4SLinus Torvalds 		 * Set <End of horizontal retrace> to minimum (0) and
7601da177e4SLinus Torvalds 		 *  <Start of horizontal Retrace> to maximum
7611da177e4SLinus Torvalds 		 * Result: turn off horizontal sync (HSync) pulse.
7621da177e4SLinus Torvalds 		 */
76398219374SKrzysztof Helt 		vga_io_wcrt(VGA_CRTC_H_SYNC_START, 0xff);
76498219374SKrzysztof Helt 		vga_io_wcrt(VGA_CRTC_H_SYNC_END, 0x00);
7651da177e4SLinus Torvalds 	}
7661da177e4SLinus Torvalds 
7671da177e4SLinus Torvalds 	/* restore both index registers */
76898219374SKrzysztof Helt 	outb_p(SeqCtrlIndex, VGA_SEQ_I);
76998219374SKrzysztof Helt 	outb_p(CrtCtrlIndex, VGA_CRT_IC);
7701da177e4SLinus Torvalds }
7711da177e4SLinus Torvalds 
vga_vesa_unblank(struct vga16fb_par * par)7721da177e4SLinus Torvalds static void vga_vesa_unblank(struct vga16fb_par *par)
7731da177e4SLinus Torvalds {
77498219374SKrzysztof Helt 	unsigned char SeqCtrlIndex = vga_io_r(VGA_SEQ_I);
77598219374SKrzysztof Helt 	unsigned char CrtCtrlIndex = vga_io_r(VGA_CRT_IC);
7761da177e4SLinus Torvalds 
7771da177e4SLinus Torvalds 	/* restore original values of VGA controller registers */
77898219374SKrzysztof Helt 	vga_io_w(VGA_MIS_W, par->vga_state.CrtMiscIO);
7791da177e4SLinus Torvalds 
7801da177e4SLinus Torvalds 	/* HorizontalTotal */
7811da177e4SLinus Torvalds 	vga_io_wcrt(0x00, par->vga_state.HorizontalTotal);
7821da177e4SLinus Torvalds 	/* HorizDisplayEnd */
7831da177e4SLinus Torvalds 	vga_io_wcrt(0x01, par->vga_state.HorizDisplayEnd);
7841da177e4SLinus Torvalds 	/* StartHorizRetrace */
7851da177e4SLinus Torvalds 	vga_io_wcrt(0x04, par->vga_state.StartHorizRetrace);
7861da177e4SLinus Torvalds 	/* EndHorizRetrace */
7871da177e4SLinus Torvalds 	vga_io_wcrt(0x05, par->vga_state.EndHorizRetrace);
7881da177e4SLinus Torvalds 	/* Overflow */
7891da177e4SLinus Torvalds 	vga_io_wcrt(0x07, par->vga_state.Overflow);
7901da177e4SLinus Torvalds 	/* StartVertRetrace */
7911da177e4SLinus Torvalds 	vga_io_wcrt(0x10, par->vga_state.StartVertRetrace);
7921da177e4SLinus Torvalds 	/* EndVertRetrace */
7931da177e4SLinus Torvalds 	vga_io_wcrt(0x11, par->vga_state.EndVertRetrace);
7941da177e4SLinus Torvalds 	/* ModeControl */
7951da177e4SLinus Torvalds 	vga_io_wcrt(0x17, par->vga_state.ModeControl);
7961da177e4SLinus Torvalds 	/* ClockingMode */
7971da177e4SLinus Torvalds 	vga_io_wseq(0x01, par->vga_state.ClockingMode);
7981da177e4SLinus Torvalds 
7991da177e4SLinus Torvalds 	/* restore index/control registers */
80098219374SKrzysztof Helt 	vga_io_w(VGA_SEQ_I, SeqCtrlIndex);
80198219374SKrzysztof Helt 	vga_io_w(VGA_CRT_IC, CrtCtrlIndex);
8021da177e4SLinus Torvalds }
8031da177e4SLinus Torvalds 
vga_pal_blank(void)8041da177e4SLinus Torvalds static void vga_pal_blank(void)
8051da177e4SLinus Torvalds {
8061da177e4SLinus Torvalds 	int i;
8071da177e4SLinus Torvalds 
8081da177e4SLinus Torvalds 	for (i=0; i<16; i++) {
80998219374SKrzysztof Helt 		outb_p(i, VGA_PEL_IW);
81098219374SKrzysztof Helt 		outb_p(0, VGA_PEL_D);
81198219374SKrzysztof Helt 		outb_p(0, VGA_PEL_D);
81298219374SKrzysztof Helt 		outb_p(0, VGA_PEL_D);
8131da177e4SLinus Torvalds 	}
8141da177e4SLinus Torvalds }
8151da177e4SLinus Torvalds 
8161da177e4SLinus Torvalds /* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
vga16fb_blank(int blank,struct fb_info * info)8171da177e4SLinus Torvalds static int vga16fb_blank(int blank, struct fb_info *info)
8181da177e4SLinus Torvalds {
819120ddb41SAntonino A. Daplas 	struct vga16fb_par *par = info->par;
8201da177e4SLinus Torvalds 
8211da177e4SLinus Torvalds 	switch (blank) {
8221da177e4SLinus Torvalds 	case FB_BLANK_UNBLANK:				/* Unblank */
8231da177e4SLinus Torvalds 		if (par->vesa_blanked) {
8241da177e4SLinus Torvalds 			vga_vesa_unblank(par);
8251da177e4SLinus Torvalds 			par->vesa_blanked = 0;
8261da177e4SLinus Torvalds 		}
8271da177e4SLinus Torvalds 		if (par->palette_blanked) {
8281da177e4SLinus Torvalds 			par->palette_blanked = 0;
8291da177e4SLinus Torvalds 		}
8301da177e4SLinus Torvalds 		break;
8311da177e4SLinus Torvalds 	case FB_BLANK_NORMAL:				/* blank */
8321da177e4SLinus Torvalds 		vga_pal_blank();
8331da177e4SLinus Torvalds 		par->palette_blanked = 1;
8341da177e4SLinus Torvalds 		break;
8351da177e4SLinus Torvalds 	default:			/* VESA blanking */
8361da177e4SLinus Torvalds 		vga_vesa_blank(par, blank);
8371da177e4SLinus Torvalds 		par->vesa_blanked = 1;
8381da177e4SLinus Torvalds 		break;
8391da177e4SLinus Torvalds 	}
8401da177e4SLinus Torvalds 	return 0;
8411da177e4SLinus Torvalds }
8421da177e4SLinus Torvalds 
vga_8planes_fillrect(struct fb_info * info,const struct fb_fillrect * rect)8431da177e4SLinus Torvalds static void vga_8planes_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
8441da177e4SLinus Torvalds {
8451da177e4SLinus Torvalds 	u32 dx = rect->dx, width = rect->width;
8461da177e4SLinus Torvalds         char oldindex = getindex();
8471da177e4SLinus Torvalds         char oldmode = setmode(0x40);
8481da177e4SLinus Torvalds         char oldmask = selectmask();
8491da177e4SLinus Torvalds         int line_ofs, height;
8501da177e4SLinus Torvalds         char oldop, oldsr;
8511da177e4SLinus Torvalds         char __iomem *where;
8521da177e4SLinus Torvalds 
8531da177e4SLinus Torvalds         dx /= 4;
8541da177e4SLinus Torvalds         where = info->screen_base + dx + rect->dy * info->fix.line_length;
8551da177e4SLinus Torvalds 
8561da177e4SLinus Torvalds         if (rect->rop == ROP_COPY) {
8571da177e4SLinus Torvalds                 oldop = setop(0);
8581da177e4SLinus Torvalds                 oldsr = setsr(0);
8591da177e4SLinus Torvalds 
8601da177e4SLinus Torvalds                 width /= 4;
8611da177e4SLinus Torvalds                 line_ofs = info->fix.line_length - width;
8621da177e4SLinus Torvalds                 setmask(0xff);
8631da177e4SLinus Torvalds 
8641da177e4SLinus Torvalds                 height = rect->height;
8651da177e4SLinus Torvalds 
8661da177e4SLinus Torvalds                 while (height--) {
8671da177e4SLinus Torvalds                         int x;
8681da177e4SLinus Torvalds 
8691da177e4SLinus Torvalds                         /* we can do memset... */
8701da177e4SLinus Torvalds                         for (x = width; x > 0; --x) {
8711da177e4SLinus Torvalds                                 writeb(rect->color, where);
8721da177e4SLinus Torvalds                                 where++;
8731da177e4SLinus Torvalds                         }
8741da177e4SLinus Torvalds                         where += line_ofs;
8751da177e4SLinus Torvalds                 }
8761da177e4SLinus Torvalds         } else {
8771da177e4SLinus Torvalds                 char oldcolor = setcolor(0xf);
8781da177e4SLinus Torvalds                 int y;
8791da177e4SLinus Torvalds 
8801da177e4SLinus Torvalds                 oldop = setop(0x18);
8811da177e4SLinus Torvalds                 oldsr = setsr(0xf);
8821da177e4SLinus Torvalds                 setmask(0x0F);
8831da177e4SLinus Torvalds                 for (y = 0; y < rect->height; y++) {
8841da177e4SLinus Torvalds                         rmw(where);
8851da177e4SLinus Torvalds                         rmw(where+1);
8861da177e4SLinus Torvalds                         where += info->fix.line_length;
8871da177e4SLinus Torvalds                 }
8881da177e4SLinus Torvalds                 setcolor(oldcolor);
8891da177e4SLinus Torvalds         }
8901da177e4SLinus Torvalds         setmask(oldmask);
8911da177e4SLinus Torvalds         setsr(oldsr);
8921da177e4SLinus Torvalds         setop(oldop);
8931da177e4SLinus Torvalds         setmode(oldmode);
8941da177e4SLinus Torvalds         setindex(oldindex);
8951da177e4SLinus Torvalds }
8961da177e4SLinus Torvalds 
vga16fb_fillrect(struct fb_info * info,const struct fb_fillrect * rect)8971da177e4SLinus Torvalds static void vga16fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
8981da177e4SLinus Torvalds {
8991da177e4SLinus Torvalds 	int x, x2, y2, vxres, vyres, width, height, line_ofs;
9001da177e4SLinus Torvalds 	char __iomem *dst;
9011da177e4SLinus Torvalds 
9021da177e4SLinus Torvalds 	vxres = info->var.xres_virtual;
9031da177e4SLinus Torvalds 	vyres = info->var.yres_virtual;
9041da177e4SLinus Torvalds 
9051da177e4SLinus Torvalds 	if (!rect->width || !rect->height || rect->dx > vxres || rect->dy > vyres)
9061da177e4SLinus Torvalds 		return;
9071da177e4SLinus Torvalds 
9081da177e4SLinus Torvalds 	/* We could use hardware clipping but on many cards you get around
9091da177e4SLinus Torvalds 	 * hardware clipping by writing to framebuffer directly. */
9101da177e4SLinus Torvalds 
9111da177e4SLinus Torvalds 	x2 = rect->dx + rect->width;
9121da177e4SLinus Torvalds 	y2 = rect->dy + rect->height;
9131da177e4SLinus Torvalds 	x2 = x2 < vxres ? x2 : vxres;
9141da177e4SLinus Torvalds 	y2 = y2 < vyres ? y2 : vyres;
9151da177e4SLinus Torvalds 	width = x2 - rect->dx;
9161da177e4SLinus Torvalds 
9171da177e4SLinus Torvalds 	switch (info->fix.type) {
9181da177e4SLinus Torvalds 	case FB_TYPE_VGA_PLANES:
9191da177e4SLinus Torvalds 		if (info->fix.type_aux == FB_AUX_VGA_PLANES_VGA4) {
9201da177e4SLinus Torvalds 
9211da177e4SLinus Torvalds 			height = y2 - rect->dy;
9221da177e4SLinus Torvalds 			width = rect->width/8;
9231da177e4SLinus Torvalds 
9241da177e4SLinus Torvalds 			line_ofs = info->fix.line_length - width;
9251da177e4SLinus Torvalds 			dst = info->screen_base + (rect->dx/8) + rect->dy * info->fix.line_length;
9261da177e4SLinus Torvalds 
9271da177e4SLinus Torvalds 			switch (rect->rop) {
9281da177e4SLinus Torvalds 			case ROP_COPY:
9291da177e4SLinus Torvalds 				setmode(0);
9301da177e4SLinus Torvalds 				setop(0);
9311da177e4SLinus Torvalds 				setsr(0xf);
9321da177e4SLinus Torvalds 				setcolor(rect->color);
9331da177e4SLinus Torvalds 				selectmask();
9341da177e4SLinus Torvalds 
9351da177e4SLinus Torvalds 				setmask(0xff);
9361da177e4SLinus Torvalds 
9371da177e4SLinus Torvalds 				while (height--) {
9381da177e4SLinus Torvalds 					for (x = 0; x < width; x++) {
9391da177e4SLinus Torvalds 						writeb(0, dst);
9401da177e4SLinus Torvalds 						dst++;
9411da177e4SLinus Torvalds 					}
9421da177e4SLinus Torvalds 					dst += line_ofs;
9431da177e4SLinus Torvalds 				}
9441da177e4SLinus Torvalds 				break;
9451da177e4SLinus Torvalds 			case ROP_XOR:
9461da177e4SLinus Torvalds 				setmode(0);
9471da177e4SLinus Torvalds 				setop(0x18);
9481da177e4SLinus Torvalds 				setsr(0xf);
9491da177e4SLinus Torvalds 				setcolor(0xf);
9501da177e4SLinus Torvalds 				selectmask();
9511da177e4SLinus Torvalds 
9521da177e4SLinus Torvalds 				setmask(0xff);
9531da177e4SLinus Torvalds 				while (height--) {
9541da177e4SLinus Torvalds 					for (x = 0; x < width; x++) {
9551da177e4SLinus Torvalds 						rmw(dst);
9561da177e4SLinus Torvalds 						dst++;
9571da177e4SLinus Torvalds 					}
9581da177e4SLinus Torvalds 					dst += line_ofs;
9591da177e4SLinus Torvalds 				}
9601da177e4SLinus Torvalds 				break;
9611da177e4SLinus Torvalds 			}
9621da177e4SLinus Torvalds 		} else
9631da177e4SLinus Torvalds 			vga_8planes_fillrect(info, rect);
9641da177e4SLinus Torvalds 		break;
9651da177e4SLinus Torvalds 	case FB_TYPE_PACKED_PIXELS:
9661da177e4SLinus Torvalds 	default:
9671da177e4SLinus Torvalds 		cfb_fillrect(info, rect);
9681da177e4SLinus Torvalds 		break;
9691da177e4SLinus Torvalds 	}
9701da177e4SLinus Torvalds }
9711da177e4SLinus Torvalds 
vga_8planes_copyarea(struct fb_info * info,const struct fb_copyarea * area)9721da177e4SLinus Torvalds static void vga_8planes_copyarea(struct fb_info *info, const struct fb_copyarea *area)
9731da177e4SLinus Torvalds {
9741da177e4SLinus Torvalds         char oldindex = getindex();
9751da177e4SLinus Torvalds         char oldmode = setmode(0x41);
9761da177e4SLinus Torvalds         char oldop = setop(0);
9771da177e4SLinus Torvalds         char oldsr = setsr(0xf);
9781da177e4SLinus Torvalds         int height, line_ofs, x;
9791da177e4SLinus Torvalds 	u32 sx, dx, width;
9801da177e4SLinus Torvalds 	char __iomem *dest;
9811da177e4SLinus Torvalds 	char __iomem *src;
9821da177e4SLinus Torvalds 
9831da177e4SLinus Torvalds         height = area->height;
9841da177e4SLinus Torvalds 
9851da177e4SLinus Torvalds         sx = area->sx / 4;
9861da177e4SLinus Torvalds         dx = area->dx / 4;
9871da177e4SLinus Torvalds         width = area->width / 4;
9881da177e4SLinus Torvalds 
9891da177e4SLinus Torvalds         if (area->dy < area->sy || (area->dy == area->sy && dx < sx)) {
9901da177e4SLinus Torvalds                 line_ofs = info->fix.line_length - width;
9911da177e4SLinus Torvalds                 dest = info->screen_base + dx + area->dy * info->fix.line_length;
9921da177e4SLinus Torvalds                 src = info->screen_base + sx + area->sy * info->fix.line_length;
9931da177e4SLinus Torvalds                 while (height--) {
9941da177e4SLinus Torvalds                         for (x = 0; x < width; x++) {
9951da177e4SLinus Torvalds                                 readb(src);
9961da177e4SLinus Torvalds                                 writeb(0, dest);
9971da177e4SLinus Torvalds                                 src++;
9981da177e4SLinus Torvalds                                 dest++;
9991da177e4SLinus Torvalds                         }
10001da177e4SLinus Torvalds                         src += line_ofs;
10011da177e4SLinus Torvalds                         dest += line_ofs;
10021da177e4SLinus Torvalds                 }
10031da177e4SLinus Torvalds         } else {
10041da177e4SLinus Torvalds                 line_ofs = info->fix.line_length - width;
10051da177e4SLinus Torvalds                 dest = info->screen_base + dx + width +
10061da177e4SLinus Torvalds 			(area->dy + height - 1) * info->fix.line_length;
10071da177e4SLinus Torvalds                 src = info->screen_base + sx + width +
10081da177e4SLinus Torvalds 			(area->sy + height - 1) * info->fix.line_length;
10091da177e4SLinus Torvalds                 while (height--) {
10101da177e4SLinus Torvalds                         for (x = 0; x < width; x++) {
10111da177e4SLinus Torvalds                                 --src;
10121da177e4SLinus Torvalds                                 --dest;
10131da177e4SLinus Torvalds                                 readb(src);
10141da177e4SLinus Torvalds                                 writeb(0, dest);
10151da177e4SLinus Torvalds                         }
10161da177e4SLinus Torvalds                         src -= line_ofs;
10171da177e4SLinus Torvalds                         dest -= line_ofs;
10181da177e4SLinus Torvalds                 }
10191da177e4SLinus Torvalds         }
10201da177e4SLinus Torvalds 
10211da177e4SLinus Torvalds         setsr(oldsr);
10221da177e4SLinus Torvalds         setop(oldop);
10231da177e4SLinus Torvalds         setmode(oldmode);
10241da177e4SLinus Torvalds         setindex(oldindex);
10251da177e4SLinus Torvalds }
10261da177e4SLinus Torvalds 
vga16fb_copyarea(struct fb_info * info,const struct fb_copyarea * area)10271da177e4SLinus Torvalds static void vga16fb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
10281da177e4SLinus Torvalds {
10291da177e4SLinus Torvalds 	u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
10301da177e4SLinus Torvalds 	int x, x2, y2, old_dx, old_dy, vxres, vyres;
10311da177e4SLinus Torvalds 	int height, width, line_ofs;
10321da177e4SLinus Torvalds 	char __iomem *dst = NULL;
10331da177e4SLinus Torvalds 	char __iomem *src = NULL;
10341da177e4SLinus Torvalds 
10351da177e4SLinus Torvalds 	vxres = info->var.xres_virtual;
10361da177e4SLinus Torvalds 	vyres = info->var.yres_virtual;
10371da177e4SLinus Torvalds 
10381da177e4SLinus Torvalds 	if (area->dx > vxres || area->sx > vxres || area->dy > vyres ||
10391da177e4SLinus Torvalds 	    area->sy > vyres)
10401da177e4SLinus Torvalds 		return;
10411da177e4SLinus Torvalds 
10421da177e4SLinus Torvalds 	/* clip the destination */
10431da177e4SLinus Torvalds 	old_dx = area->dx;
10441da177e4SLinus Torvalds 	old_dy = area->dy;
10451da177e4SLinus Torvalds 
10461da177e4SLinus Torvalds 	/*
10471da177e4SLinus Torvalds 	 * We could use hardware clipping but on many cards you get around
10481da177e4SLinus Torvalds 	 * hardware clipping by writing to framebuffer directly.
10491da177e4SLinus Torvalds 	 */
10501da177e4SLinus Torvalds 	x2 = area->dx + area->width;
10511da177e4SLinus Torvalds 	y2 = area->dy + area->height;
10521da177e4SLinus Torvalds 	dx = area->dx > 0 ? area->dx : 0;
10531da177e4SLinus Torvalds 	dy = area->dy > 0 ? area->dy : 0;
10541da177e4SLinus Torvalds 	x2 = x2 < vxres ? x2 : vxres;
10551da177e4SLinus Torvalds 	y2 = y2 < vyres ? y2 : vyres;
10561da177e4SLinus Torvalds 	width = x2 - dx;
10571da177e4SLinus Torvalds 	height = y2 - dy;
10581da177e4SLinus Torvalds 
105977a6e7abSRoel Kluin 	if (sx + dx < old_dx || sy + dy < old_dy)
106077a6e7abSRoel Kluin 		return;
106177a6e7abSRoel Kluin 
10621da177e4SLinus Torvalds 	/* update sx1,sy1 */
10631da177e4SLinus Torvalds 	sx += (dx - old_dx);
10641da177e4SLinus Torvalds 	sy += (dy - old_dy);
10651da177e4SLinus Torvalds 
10661da177e4SLinus Torvalds 	/* the source must be completely inside the virtual screen */
106777a6e7abSRoel Kluin 	if (sx + width > vxres || sy + height > vyres)
10681da177e4SLinus Torvalds 		return;
10691da177e4SLinus Torvalds 
10701da177e4SLinus Torvalds 	switch (info->fix.type) {
10711da177e4SLinus Torvalds 	case FB_TYPE_VGA_PLANES:
10721da177e4SLinus Torvalds 		if (info->fix.type_aux == FB_AUX_VGA_PLANES_VGA4) {
10731da177e4SLinus Torvalds 			width = width/8;
10741da177e4SLinus Torvalds 			line_ofs = info->fix.line_length - width;
10751da177e4SLinus Torvalds 
10761da177e4SLinus Torvalds 			setmode(1);
10771da177e4SLinus Torvalds 			setop(0);
10781da177e4SLinus Torvalds 			setsr(0xf);
10791da177e4SLinus Torvalds 
10801da177e4SLinus Torvalds 			if (dy < sy || (dy == sy && dx < sx)) {
10811da177e4SLinus Torvalds 				dst = info->screen_base + (dx/8) + dy * info->fix.line_length;
10821da177e4SLinus Torvalds 				src = info->screen_base + (sx/8) + sy * info->fix.line_length;
10831da177e4SLinus Torvalds 				while (height--) {
10841da177e4SLinus Torvalds 					for (x = 0; x < width; x++) {
10851da177e4SLinus Torvalds 						readb(src);
10861da177e4SLinus Torvalds 						writeb(0, dst);
10871da177e4SLinus Torvalds 						dst++;
10881da177e4SLinus Torvalds 						src++;
10891da177e4SLinus Torvalds 					}
10901da177e4SLinus Torvalds 					src += line_ofs;
10911da177e4SLinus Torvalds 					dst += line_ofs;
10921da177e4SLinus Torvalds 				}
10931da177e4SLinus Torvalds 			} else {
10941da177e4SLinus Torvalds 				dst = info->screen_base + (dx/8) + width +
10951da177e4SLinus Torvalds 					(dy + height - 1) * info->fix.line_length;
10961da177e4SLinus Torvalds 				src = info->screen_base + (sx/8) + width +
10971da177e4SLinus Torvalds 					(sy + height  - 1) * info->fix.line_length;
10981da177e4SLinus Torvalds 				while (height--) {
10991da177e4SLinus Torvalds 					for (x = 0; x < width; x++) {
11001da177e4SLinus Torvalds 						dst--;
11011da177e4SLinus Torvalds 						src--;
11021da177e4SLinus Torvalds 						readb(src);
11031da177e4SLinus Torvalds 						writeb(0, dst);
11041da177e4SLinus Torvalds 					}
11051da177e4SLinus Torvalds 					src -= line_ofs;
11061da177e4SLinus Torvalds 					dst -= line_ofs;
11071da177e4SLinus Torvalds 				}
11081da177e4SLinus Torvalds 			}
11091da177e4SLinus Torvalds 		} else
11101da177e4SLinus Torvalds 			vga_8planes_copyarea(info, area);
11111da177e4SLinus Torvalds 		break;
11121da177e4SLinus Torvalds 	case FB_TYPE_PACKED_PIXELS:
11131da177e4SLinus Torvalds 	default:
11141da177e4SLinus Torvalds 		cfb_copyarea(info, area);
11151da177e4SLinus Torvalds 		break;
11161da177e4SLinus Torvalds 	}
11171da177e4SLinus Torvalds }
11181da177e4SLinus Torvalds 
1119ec1a7b3dSHelge Deller #define TRANS_MASK_LOW  {0x0,0x8,0x4,0xC,0x2,0xA,0x6,0xE,0x1,0x9,0x5,0xD,0x3,0xB,0x7,0xF}
1120ec1a7b3dSHelge Deller #define TRANS_MASK_HIGH {0x000, 0x800, 0x400, 0xC00, 0x200, 0xA00, 0x600, 0xE00, \
1121ec1a7b3dSHelge Deller 			 0x100, 0x900, 0x500, 0xD00, 0x300, 0xB00, 0x700, 0xF00}
1122ec1a7b3dSHelge Deller 
1123ec1a7b3dSHelge Deller #if defined(__LITTLE_ENDIAN)
1124ec1a7b3dSHelge Deller static const u16 transl_l[] = TRANS_MASK_LOW;
1125ec1a7b3dSHelge Deller static const u16 transl_h[] = TRANS_MASK_HIGH;
1126ec1a7b3dSHelge Deller #elif defined(__BIG_ENDIAN)
1127ec1a7b3dSHelge Deller static const u16 transl_l[] = TRANS_MASK_HIGH;
1128ec1a7b3dSHelge Deller static const u16 transl_h[] = TRANS_MASK_LOW;
11291da177e4SLinus Torvalds #else
11301da177e4SLinus Torvalds #error "Only __BIG_ENDIAN and __LITTLE_ENDIAN are supported in vga-planes"
11311da177e4SLinus Torvalds #endif
11321da177e4SLinus Torvalds 
vga_8planes_imageblit(struct fb_info * info,const struct fb_image * image)11331da177e4SLinus Torvalds static void vga_8planes_imageblit(struct fb_info *info, const struct fb_image *image)
11341da177e4SLinus Torvalds {
11351da177e4SLinus Torvalds         char oldindex = getindex();
11361da177e4SLinus Torvalds         char oldmode = setmode(0x40);
11371da177e4SLinus Torvalds         char oldop = setop(0);
11381da177e4SLinus Torvalds         char oldsr = setsr(0);
11391da177e4SLinus Torvalds         char oldmask = selectmask();
1140bd018a6aSTetsuo Handa 	const unsigned char *cdat = image->data;
11411da177e4SLinus Torvalds 	u32 dx = image->dx;
11421da177e4SLinus Torvalds         char __iomem *where;
11431da177e4SLinus Torvalds         int y;
11441da177e4SLinus Torvalds 
11451da177e4SLinus Torvalds         dx /= 4;
11461da177e4SLinus Torvalds         where = info->screen_base + dx + image->dy * info->fix.line_length;
11471da177e4SLinus Torvalds 
11481da177e4SLinus Torvalds         setmask(0xff);
11491da177e4SLinus Torvalds         writeb(image->bg_color, where);
11501da177e4SLinus Torvalds         readb(where);
11511da177e4SLinus Torvalds         selectmask();
11521da177e4SLinus Torvalds         setmask(image->fg_color ^ image->bg_color);
11531da177e4SLinus Torvalds         setmode(0x42);
11541da177e4SLinus Torvalds         setop(0x18);
11551da177e4SLinus Torvalds         for (y = 0; y < image->height; y++, where += info->fix.line_length)
11561da177e4SLinus Torvalds                 writew(transl_h[cdat[y]&0xF] | transl_l[cdat[y] >> 4], where);
11571da177e4SLinus Torvalds         setmask(oldmask);
11581da177e4SLinus Torvalds         setsr(oldsr);
11591da177e4SLinus Torvalds         setop(oldop);
11601da177e4SLinus Torvalds         setmode(oldmode);
11611da177e4SLinus Torvalds         setindex(oldindex);
11621da177e4SLinus Torvalds }
11631da177e4SLinus Torvalds 
vga_imageblit_expand(struct fb_info * info,const struct fb_image * image)11641da177e4SLinus Torvalds static void vga_imageblit_expand(struct fb_info *info, const struct fb_image *image)
11651da177e4SLinus Torvalds {
11661da177e4SLinus Torvalds 	char __iomem *where = info->screen_base + (image->dx/8) +
11671da177e4SLinus Torvalds 		image->dy * info->fix.line_length;
1168120ddb41SAntonino A. Daplas 	struct vga16fb_par *par = info->par;
11691da177e4SLinus Torvalds 	char *cdat = (char *) image->data;
11701da177e4SLinus Torvalds 	char __iomem *dst;
11711da177e4SLinus Torvalds 	int x, y;
11721da177e4SLinus Torvalds 
11731da177e4SLinus Torvalds 	switch (info->fix.type) {
11741da177e4SLinus Torvalds 	case FB_TYPE_VGA_PLANES:
11751da177e4SLinus Torvalds 		if (info->fix.type_aux == FB_AUX_VGA_PLANES_VGA4) {
11761da177e4SLinus Torvalds 			if (par->isVGA) {
11771da177e4SLinus Torvalds 				setmode(2);
11781da177e4SLinus Torvalds 				setop(0);
11791da177e4SLinus Torvalds 				setsr(0xf);
11801da177e4SLinus Torvalds 				setcolor(image->fg_color);
11811da177e4SLinus Torvalds 				selectmask();
11821da177e4SLinus Torvalds 
11831da177e4SLinus Torvalds 				setmask(0xff);
11841da177e4SLinus Torvalds 				writeb(image->bg_color, where);
11851da177e4SLinus Torvalds 				rmb();
11861da177e4SLinus Torvalds 				readb(where); /* fill latches */
11871da177e4SLinus Torvalds 				setmode(3);
11881da177e4SLinus Torvalds 				wmb();
11891da177e4SLinus Torvalds 				for (y = 0; y < image->height; y++) {
11901da177e4SLinus Torvalds 					dst = where;
11911da177e4SLinus Torvalds 					for (x = image->width/8; x--;)
11921da177e4SLinus Torvalds 						writeb(*cdat++, dst++);
11931da177e4SLinus Torvalds 					where += info->fix.line_length;
11941da177e4SLinus Torvalds 				}
11951da177e4SLinus Torvalds 				wmb();
11961da177e4SLinus Torvalds 			} else {
11971da177e4SLinus Torvalds 				setmode(0);
11981da177e4SLinus Torvalds 				setop(0);
11991da177e4SLinus Torvalds 				setsr(0xf);
12001da177e4SLinus Torvalds 				setcolor(image->bg_color);
12011da177e4SLinus Torvalds 				selectmask();
12021da177e4SLinus Torvalds 
12031da177e4SLinus Torvalds 				setmask(0xff);
12041da177e4SLinus Torvalds 				for (y = 0; y < image->height; y++) {
12051da177e4SLinus Torvalds 					dst = where;
12061da177e4SLinus Torvalds 					for (x=image->width/8; x--;){
12071da177e4SLinus Torvalds 						rmw(dst);
12081da177e4SLinus Torvalds 						setcolor(image->fg_color);
12091da177e4SLinus Torvalds 						selectmask();
12101da177e4SLinus Torvalds 						if (*cdat) {
12111da177e4SLinus Torvalds 							setmask(*cdat++);
12121da177e4SLinus Torvalds 							rmw(dst++);
12131da177e4SLinus Torvalds 						}
12141da177e4SLinus Torvalds 					}
12151da177e4SLinus Torvalds 					where += info->fix.line_length;
12161da177e4SLinus Torvalds 				}
12171da177e4SLinus Torvalds 			}
12181da177e4SLinus Torvalds 		} else
12191da177e4SLinus Torvalds 			vga_8planes_imageblit(info, image);
12201da177e4SLinus Torvalds 		break;
12211da177e4SLinus Torvalds 	case FB_TYPE_PACKED_PIXELS:
12221da177e4SLinus Torvalds 	default:
12231da177e4SLinus Torvalds 		cfb_imageblit(info, image);
12241da177e4SLinus Torvalds 		break;
12251da177e4SLinus Torvalds 	}
12261da177e4SLinus Torvalds }
12271da177e4SLinus Torvalds 
vga_imageblit_color(struct fb_info * info,const struct fb_image * image)12281da177e4SLinus Torvalds static void vga_imageblit_color(struct fb_info *info, const struct fb_image *image)
12291da177e4SLinus Torvalds {
12301da177e4SLinus Torvalds 	/*
12311da177e4SLinus Torvalds 	 * Draw logo
12321da177e4SLinus Torvalds 	 */
1233120ddb41SAntonino A. Daplas 	struct vga16fb_par *par = info->par;
12341da177e4SLinus Torvalds 	char __iomem *where =
12351da177e4SLinus Torvalds 		info->screen_base + image->dy * info->fix.line_length +
12361da177e4SLinus Torvalds 		image->dx/8;
12371da177e4SLinus Torvalds 	const char *cdat = image->data;
12381da177e4SLinus Torvalds 	char __iomem *dst;
12391da177e4SLinus Torvalds 	int x, y;
12401da177e4SLinus Torvalds 
12411da177e4SLinus Torvalds 	switch (info->fix.type) {
12421da177e4SLinus Torvalds 	case FB_TYPE_VGA_PLANES:
12431da177e4SLinus Torvalds 		if (info->fix.type_aux == FB_AUX_VGA_PLANES_VGA4 &&
12441da177e4SLinus Torvalds 		    par->isVGA) {
12451da177e4SLinus Torvalds 			setsr(0xf);
12461da177e4SLinus Torvalds 			setop(0);
12471da177e4SLinus Torvalds 			setmode(0);
12481da177e4SLinus Torvalds 
12491da177e4SLinus Torvalds 			for (y = 0; y < image->height; y++) {
12501da177e4SLinus Torvalds 				for (x = 0; x < image->width; x++) {
12511da177e4SLinus Torvalds 					dst = where + x/8;
12521da177e4SLinus Torvalds 
12531da177e4SLinus Torvalds 					setcolor(*cdat);
12541da177e4SLinus Torvalds 					selectmask();
12551da177e4SLinus Torvalds 					setmask(1 << (7 - (x % 8)));
12561da177e4SLinus Torvalds 					fb_readb(dst);
12571da177e4SLinus Torvalds 					fb_writeb(0, dst);
12581da177e4SLinus Torvalds 
12591da177e4SLinus Torvalds 					cdat++;
12601da177e4SLinus Torvalds 				}
12611da177e4SLinus Torvalds 				where += info->fix.line_length;
12621da177e4SLinus Torvalds 			}
12631da177e4SLinus Torvalds 		}
12641da177e4SLinus Torvalds 		break;
12651da177e4SLinus Torvalds 	case FB_TYPE_PACKED_PIXELS:
12661da177e4SLinus Torvalds 		cfb_imageblit(info, image);
12671da177e4SLinus Torvalds 		break;
12681da177e4SLinus Torvalds 	default:
12691da177e4SLinus Torvalds 		break;
12701da177e4SLinus Torvalds 	}
12711da177e4SLinus Torvalds }
12721da177e4SLinus Torvalds 
vga16fb_imageblit(struct fb_info * info,const struct fb_image * image)12731da177e4SLinus Torvalds static void vga16fb_imageblit(struct fb_info *info, const struct fb_image *image)
12741da177e4SLinus Torvalds {
12751da177e4SLinus Torvalds 	if (image->depth == 1)
12761da177e4SLinus Torvalds 		vga_imageblit_expand(info, image);
12771da177e4SLinus Torvalds 	else
12781da177e4SLinus Torvalds 		vga_imageblit_color(info, image);
12791da177e4SLinus Torvalds }
12801da177e4SLinus Torvalds 
vga16fb_destroy(struct fb_info * info)12813b9676e7SMarcin Slusarz static void vga16fb_destroy(struct fb_info *info)
12823b9676e7SMarcin Slusarz {
12833b9676e7SMarcin Slusarz 	iounmap(info->screen_base);
12843b9676e7SMarcin Slusarz 	fb_dealloc_cmap(&info->cmap);
12853b9676e7SMarcin Slusarz 	/* XXX unshare VGA regions */
12863b9676e7SMarcin Slusarz 	framebuffer_release(info);
12873b9676e7SMarcin Slusarz }
12883b9676e7SMarcin Slusarz 
12898a48ac33SJani Nikula static const struct fb_ops vga16fb_ops = {
12901da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
12911da177e4SLinus Torvalds 	.fb_open        = vga16fb_open,
12921da177e4SLinus Torvalds 	.fb_release     = vga16fb_release,
1293e47e199cSThomas Zimmermann 	__FB_DEFAULT_IOMEM_OPS_RDWR,
12943b9676e7SMarcin Slusarz 	.fb_destroy	= vga16fb_destroy,
12951da177e4SLinus Torvalds 	.fb_check_var	= vga16fb_check_var,
12961da177e4SLinus Torvalds 	.fb_set_par	= vga16fb_set_par,
12971da177e4SLinus Torvalds 	.fb_setcolreg 	= vga16fb_setcolreg,
12981da177e4SLinus Torvalds 	.fb_pan_display = vga16fb_pan_display,
12991da177e4SLinus Torvalds 	.fb_blank 	= vga16fb_blank,
13001da177e4SLinus Torvalds 	.fb_fillrect	= vga16fb_fillrect,
13011da177e4SLinus Torvalds 	.fb_copyarea	= vga16fb_copyarea,
13021da177e4SLinus Torvalds 	.fb_imageblit	= vga16fb_imageblit,
1303e47e199cSThomas Zimmermann 	__FB_DEFAULT_IOMEM_OPS_MMAP,
13041da177e4SLinus Torvalds };
13051da177e4SLinus Torvalds 
vga16fb_probe(struct platform_device * dev)130648c68c4fSGreg Kroah-Hartman static int vga16fb_probe(struct platform_device *dev)
1307120ddb41SAntonino A. Daplas {
13080db5b61eSThomas Zimmermann 	struct screen_info *si;
1309120ddb41SAntonino A. Daplas 	struct fb_info *info;
1310120ddb41SAntonino A. Daplas 	struct vga16fb_par *par;
1311120ddb41SAntonino A. Daplas 	int i;
1312120ddb41SAntonino A. Daplas 	int ret = 0;
1313120ddb41SAntonino A. Daplas 
13140db5b61eSThomas Zimmermann 	si = dev_get_platdata(&dev->dev);
13150db5b61eSThomas Zimmermann 	if (!si)
13160db5b61eSThomas Zimmermann 		return -ENODEV;
13170db5b61eSThomas Zimmermann 
13180db5b61eSThomas Zimmermann 	ret = check_mode_supported(si);
13190db5b61eSThomas Zimmermann 	if (ret)
13200db5b61eSThomas Zimmermann 		return ret;
13210db5b61eSThomas Zimmermann 
1322120ddb41SAntonino A. Daplas 	printk(KERN_DEBUG "vga16fb: initializing\n");
1323120ddb41SAntonino A. Daplas 	info = framebuffer_alloc(sizeof(struct vga16fb_par), &dev->dev);
1324120ddb41SAntonino A. Daplas 
1325120ddb41SAntonino A. Daplas 	if (!info) {
1326120ddb41SAntonino A. Daplas 		ret = -ENOMEM;
1327120ddb41SAntonino A. Daplas 		goto err_fb_alloc;
1328120ddb41SAntonino A. Daplas 	}
1329120ddb41SAntonino A. Daplas 
13304652905fSThomas Zimmermann 	/* XXX share VGA_FB_PHYS_BASE and I/O region with vgacon and others */
13314652905fSThomas Zimmermann 	info->screen_base = (void __iomem *)VGA_MAP_MEM(VGA_FB_PHYS_BASE, 0);
1332120ddb41SAntonino A. Daplas 
1333120ddb41SAntonino A. Daplas 	if (!info->screen_base) {
1334120ddb41SAntonino A. Daplas 		printk(KERN_ERR "vga16fb: unable to map device\n");
1335120ddb41SAntonino A. Daplas 		ret = -ENOMEM;
1336120ddb41SAntonino A. Daplas 		goto err_ioremap;
1337120ddb41SAntonino A. Daplas 	}
1338120ddb41SAntonino A. Daplas 
1339120ddb41SAntonino A. Daplas 	printk(KERN_INFO "vga16fb: mapped to 0x%p\n", info->screen_base);
1340120ddb41SAntonino A. Daplas 	par = info->par;
1341120ddb41SAntonino A. Daplas 
1342*c542a56aSZsolt Kajtar 	par->isVGA = screen_info_video_type(si) == VIDEO_TYPE_VGAC;
1343120ddb41SAntonino A. Daplas 	par->palette_blanked = 0;
1344120ddb41SAntonino A. Daplas 	par->vesa_blanked = 0;
1345120ddb41SAntonino A. Daplas 
1346120ddb41SAntonino A. Daplas 	i = par->isVGA? 6 : 2;
1347120ddb41SAntonino A. Daplas 
1348120ddb41SAntonino A. Daplas 	vga16fb_defined.red.length   = i;
1349120ddb41SAntonino A. Daplas 	vga16fb_defined.green.length = i;
1350120ddb41SAntonino A. Daplas 	vga16fb_defined.blue.length  = i;
1351120ddb41SAntonino A. Daplas 
1352120ddb41SAntonino A. Daplas 	/* name should not depend on EGA/VGA */
1353120ddb41SAntonino A. Daplas 	info->fbops = &vga16fb_ops;
1354120ddb41SAntonino A. Daplas 	info->var = vga16fb_defined;
1355120ddb41SAntonino A. Daplas 	info->fix = vga16fb_fix;
13567e645ffdSAntonino A. Daplas 	/* supports rectangles with widths of multiples of 8 */
135715260979SSamuel Thibault 	bitmap_zero(info->pixmap.blit_x, FB_MAX_BLIT_WIDTH);
135815260979SSamuel Thibault 	set_bit(8 - 1, info->pixmap.blit_x);
135915260979SSamuel Thibault 	set_bit(16 - 1, info->pixmap.blit_x);
136015260979SSamuel Thibault 	set_bit(24 - 1, info->pixmap.blit_x);
136115260979SSamuel Thibault 	set_bit(32 - 1, info->pixmap.blit_x);
13628a4675ebSThomas Zimmermann 	info->flags = FBINFO_HWACCEL_YPAN;
1363120ddb41SAntonino A. Daplas 
1364120ddb41SAntonino A. Daplas 	i = (info->var.bits_per_pixel == 8) ? 256 : 16;
1365120ddb41SAntonino A. Daplas 	ret = fb_alloc_cmap(&info->cmap, i, 0);
1366120ddb41SAntonino A. Daplas 	if (ret) {
1367120ddb41SAntonino A. Daplas 		printk(KERN_ERR "vga16fb: unable to allocate colormap\n");
1368120ddb41SAntonino A. Daplas 		ret = -ENOMEM;
1369120ddb41SAntonino A. Daplas 		goto err_alloc_cmap;
1370120ddb41SAntonino A. Daplas 	}
1371120ddb41SAntonino A. Daplas 
1372120ddb41SAntonino A. Daplas 	if (vga16fb_check_var(&info->var, info)) {
1373120ddb41SAntonino A. Daplas 		printk(KERN_ERR "vga16fb: unable to validate variable\n");
1374120ddb41SAntonino A. Daplas 		ret = -EINVAL;
1375120ddb41SAntonino A. Daplas 		goto err_check_var;
1376120ddb41SAntonino A. Daplas 	}
1377120ddb41SAntonino A. Daplas 
1378120ddb41SAntonino A. Daplas 	vga16fb_update_fix(info);
1379120ddb41SAntonino A. Daplas 
13802cb14c86SThomas Zimmermann 	ret = devm_aperture_acquire_for_platform_device(dev, VGA_FB_PHYS_BASE, VGA_FB_PHYS_SIZE);
13812cb14c86SThomas Zimmermann 	if (ret)
13822cb14c86SThomas Zimmermann 		goto err_check_var;
1383120ddb41SAntonino A. Daplas 	if (register_framebuffer(info) < 0) {
1384120ddb41SAntonino A. Daplas 		printk(KERN_ERR "vga16fb: unable to register framebuffer\n");
1385120ddb41SAntonino A. Daplas 		ret = -EINVAL;
1386120ddb41SAntonino A. Daplas 		goto err_check_var;
1387120ddb41SAntonino A. Daplas 	}
1388120ddb41SAntonino A. Daplas 
138931b6780cSJoe Perches 	fb_info(info, "%s frame buffer device\n", info->fix.id);
1390ae6d3218SAntonino A. Daplas 	platform_set_drvdata(dev, info);
1391120ddb41SAntonino A. Daplas 
1392120ddb41SAntonino A. Daplas 	return 0;
1393120ddb41SAntonino A. Daplas 
1394120ddb41SAntonino A. Daplas  err_check_var:
1395120ddb41SAntonino A. Daplas 	fb_dealloc_cmap(&info->cmap);
1396120ddb41SAntonino A. Daplas  err_alloc_cmap:
1397120ddb41SAntonino A. Daplas 	iounmap(info->screen_base);
1398120ddb41SAntonino A. Daplas  err_ioremap:
1399120ddb41SAntonino A. Daplas 	framebuffer_release(info);
1400120ddb41SAntonino A. Daplas  err_fb_alloc:
1401120ddb41SAntonino A. Daplas 	return ret;
1402120ddb41SAntonino A. Daplas }
1403120ddb41SAntonino A. Daplas 
vga16fb_remove(struct platform_device * dev)14044a5ef62cSUwe Kleine-König static void vga16fb_remove(struct platform_device *dev)
1405120ddb41SAntonino A. Daplas {
1406ae6d3218SAntonino A. Daplas 	struct fb_info *info = platform_get_drvdata(dev);
1407120ddb41SAntonino A. Daplas 
14083b9676e7SMarcin Slusarz 	if (info)
1409120ddb41SAntonino A. Daplas 		unregister_framebuffer(info);
1410120ddb41SAntonino A. Daplas }
1411120ddb41SAntonino A. Daplas 
14120db5b61eSThomas Zimmermann static const struct platform_device_id vga16fb_driver_id_table[] = {
14130db5b61eSThomas Zimmermann 	{"ega-framebuffer", 0},
14140db5b61eSThomas Zimmermann 	{"vga-framebuffer", 0},
14150db5b61eSThomas Zimmermann 	{ }
14160db5b61eSThomas Zimmermann };
14173b29f36eSZeng Heng MODULE_DEVICE_TABLE(platform, vga16fb_driver_id_table);
14180db5b61eSThomas Zimmermann 
1419ae6d3218SAntonino A. Daplas static struct platform_driver vga16fb_driver = {
1420120ddb41SAntonino A. Daplas 	.probe = vga16fb_probe,
142101ecc142SUwe Kleine-König 	.remove = vga16fb_remove,
1422ae6d3218SAntonino A. Daplas 	.driver = {
1423ae6d3218SAntonino A. Daplas 		.name = "vga16fb",
1424ae6d3218SAntonino A. Daplas 	},
14250db5b61eSThomas Zimmermann 	.id_table = vga16fb_driver_id_table,
1426120ddb41SAntonino A. Daplas };
1427120ddb41SAntonino A. Daplas 
14288a611e08SThomas Zimmermann module_platform_driver(vga16fb_driver);
14291da177e4SLinus Torvalds 
143098219374SKrzysztof Helt MODULE_DESCRIPTION("Legacy VGA framebuffer device driver");
14311da177e4SLinus Torvalds MODULE_LICENSE("GPL");
1432