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