162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/drivers/video/hgafb.c -- Hercules graphics adaptor frame buffer device 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Created 25 Nov 1999 by Ferenc Bakonyi (fero@drama.obuda.kando.hu) 562306a36Sopenharmony_ci * Based on skeletonfb.c by Geert Uytterhoeven and 662306a36Sopenharmony_ci * mdacon.c by Andrew Apted 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * History: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * - Revision 0.1.8 (23 Oct 2002): Ported to new framebuffer api. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * - Revision 0.1.7 (23 Jan 2001): fix crash resulting from MDA only cards 1362306a36Sopenharmony_ci * being detected as Hercules. (Paul G.) 1462306a36Sopenharmony_ci * - Revision 0.1.6 (17 Aug 2000): new style structs 1562306a36Sopenharmony_ci * documentation 1662306a36Sopenharmony_ci * - Revision 0.1.5 (13 Mar 2000): spinlocks instead of saveflags();cli();etc 1762306a36Sopenharmony_ci * minor fixes 1862306a36Sopenharmony_ci * - Revision 0.1.4 (24 Jan 2000): fixed a bug in hga_card_detect() for 1962306a36Sopenharmony_ci * HGA-only systems 2062306a36Sopenharmony_ci * - Revision 0.1.3 (22 Jan 2000): modified for the new fb_info structure 2162306a36Sopenharmony_ci * screen is cleared after rmmod 2262306a36Sopenharmony_ci * virtual resolutions 2362306a36Sopenharmony_ci * module parameter 'nologo={0|1}' 2462306a36Sopenharmony_ci * the most important: boot logo :) 2562306a36Sopenharmony_ci * - Revision 0.1.0 (6 Dec 1999): faster scrolling and minor fixes 2662306a36Sopenharmony_ci * - First release (25 Nov 1999) 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 2962306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive 3062306a36Sopenharmony_ci * for more details. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <linux/module.h> 3462306a36Sopenharmony_ci#include <linux/kernel.h> 3562306a36Sopenharmony_ci#include <linux/errno.h> 3662306a36Sopenharmony_ci#include <linux/spinlock.h> 3762306a36Sopenharmony_ci#include <linux/string.h> 3862306a36Sopenharmony_ci#include <linux/mm.h> 3962306a36Sopenharmony_ci#include <linux/delay.h> 4062306a36Sopenharmony_ci#include <linux/fb.h> 4162306a36Sopenharmony_ci#include <linux/init.h> 4262306a36Sopenharmony_ci#include <linux/ioport.h> 4362306a36Sopenharmony_ci#include <linux/platform_device.h> 4462306a36Sopenharmony_ci#include <asm/io.h> 4562306a36Sopenharmony_ci#include <asm/vga.h> 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#if 0 4862306a36Sopenharmony_ci#define DPRINTK(args...) printk(KERN_DEBUG __FILE__": " ##args) 4962306a36Sopenharmony_ci#else 5062306a36Sopenharmony_ci#define DPRINTK(args...) 5162306a36Sopenharmony_ci#endif 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#if 0 5462306a36Sopenharmony_ci#define CHKINFO(ret) if (info != &fb_info) { printk(KERN_DEBUG __FILE__": This should never happen, line:%d \n", __LINE__); return ret; } 5562306a36Sopenharmony_ci#else 5662306a36Sopenharmony_ci#define CHKINFO(ret) 5762306a36Sopenharmony_ci#endif 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* Description of the hardware layout */ 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic void __iomem *hga_vram; /* Base of video memory */ 6262306a36Sopenharmony_cistatic unsigned long hga_vram_len; /* Size of video memory */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define HGA_ROWADDR(row) ((row%4)*8192 + (row>>2)*90) 6562306a36Sopenharmony_ci#define HGA_TXT 0 6662306a36Sopenharmony_ci#define HGA_GFX 1 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic inline u8 __iomem * rowaddr(struct fb_info *info, u_int row) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci return info->screen_base + HGA_ROWADDR(row); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int hga_mode = -1; /* 0 = txt, 1 = gfx mode */ 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic enum { TYPE_HERC, TYPE_HERCPLUS, TYPE_HERCCOLOR } hga_type; 7662306a36Sopenharmony_cistatic char *hga_type_name; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define HGA_INDEX_PORT 0x3b4 /* Register select port */ 7962306a36Sopenharmony_ci#define HGA_VALUE_PORT 0x3b5 /* Register value port */ 8062306a36Sopenharmony_ci#define HGA_MODE_PORT 0x3b8 /* Mode control port */ 8162306a36Sopenharmony_ci#define HGA_STATUS_PORT 0x3ba /* Status and Config port */ 8262306a36Sopenharmony_ci#define HGA_GFX_PORT 0x3bf /* Graphics control port */ 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* HGA register values */ 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define HGA_CURSOR_BLINKING 0x00 8762306a36Sopenharmony_ci#define HGA_CURSOR_OFF 0x20 8862306a36Sopenharmony_ci#define HGA_CURSOR_SLOWBLINK 0x60 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define HGA_MODE_GRAPHICS 0x02 9162306a36Sopenharmony_ci#define HGA_MODE_VIDEO_EN 0x08 9262306a36Sopenharmony_ci#define HGA_MODE_BLINK_EN 0x20 9362306a36Sopenharmony_ci#define HGA_MODE_GFX_PAGE1 0x80 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define HGA_STATUS_HSYNC 0x01 9662306a36Sopenharmony_ci#define HGA_STATUS_VSYNC 0x80 9762306a36Sopenharmony_ci#define HGA_STATUS_VIDEO 0x08 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define HGA_CONFIG_COL132 0x08 10062306a36Sopenharmony_ci#define HGA_GFX_MODE_EN 0x01 10162306a36Sopenharmony_ci#define HGA_GFX_PAGE_EN 0x02 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* Global locks */ 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(hga_reg_lock); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* Framebuffer driver structures */ 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic const struct fb_var_screeninfo hga_default_var = { 11062306a36Sopenharmony_ci .xres = 720, 11162306a36Sopenharmony_ci .yres = 348, 11262306a36Sopenharmony_ci .xres_virtual = 720, 11362306a36Sopenharmony_ci .yres_virtual = 348, 11462306a36Sopenharmony_ci .bits_per_pixel = 1, 11562306a36Sopenharmony_ci .red = {0, 1, 0}, 11662306a36Sopenharmony_ci .green = {0, 1, 0}, 11762306a36Sopenharmony_ci .blue = {0, 1, 0}, 11862306a36Sopenharmony_ci .transp = {0, 0, 0}, 11962306a36Sopenharmony_ci .height = -1, 12062306a36Sopenharmony_ci .width = -1, 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic struct fb_fix_screeninfo hga_fix = { 12462306a36Sopenharmony_ci .id = "HGA", 12562306a36Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, /* (not sure) */ 12662306a36Sopenharmony_ci .visual = FB_VISUAL_MONO10, 12762306a36Sopenharmony_ci .xpanstep = 8, 12862306a36Sopenharmony_ci .ypanstep = 8, 12962306a36Sopenharmony_ci .line_length = 90, 13062306a36Sopenharmony_ci .accel = FB_ACCEL_NONE 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* Don't assume that tty1 will be the initial current console. */ 13462306a36Sopenharmony_cistatic int release_io_port = 0; 13562306a36Sopenharmony_cistatic int release_io_ports = 0; 13662306a36Sopenharmony_cistatic bool nologo = 0; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/* ------------------------------------------------------------------------- 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * Low level hardware functions 14162306a36Sopenharmony_ci * 14262306a36Sopenharmony_ci * ------------------------------------------------------------------------- */ 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void write_hga_b(unsigned int val, unsigned char reg) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci outb_p(reg, HGA_INDEX_PORT); 14762306a36Sopenharmony_ci outb_p(val, HGA_VALUE_PORT); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic void write_hga_w(unsigned int val, unsigned char reg) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci outb_p(reg, HGA_INDEX_PORT); outb_p(val >> 8, HGA_VALUE_PORT); 15362306a36Sopenharmony_ci outb_p(reg+1, HGA_INDEX_PORT); outb_p(val & 0xff, HGA_VALUE_PORT); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int test_hga_b(unsigned char val, unsigned char reg) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci outb_p(reg, HGA_INDEX_PORT); 15962306a36Sopenharmony_ci outb (val, HGA_VALUE_PORT); 16062306a36Sopenharmony_ci udelay(20); val = (inb_p(HGA_VALUE_PORT) == val); 16162306a36Sopenharmony_ci return val; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void hga_clear_screen(void) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci unsigned char fillchar = 0xbf; /* magic */ 16762306a36Sopenharmony_ci unsigned long flags; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci spin_lock_irqsave(&hga_reg_lock, flags); 17062306a36Sopenharmony_ci if (hga_mode == HGA_TXT) 17162306a36Sopenharmony_ci fillchar = ' '; 17262306a36Sopenharmony_ci else if (hga_mode == HGA_GFX) 17362306a36Sopenharmony_ci fillchar = 0x00; 17462306a36Sopenharmony_ci spin_unlock_irqrestore(&hga_reg_lock, flags); 17562306a36Sopenharmony_ci if (fillchar != 0xbf) 17662306a36Sopenharmony_ci memset_io(hga_vram, fillchar, hga_vram_len); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void hga_txt_mode(void) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci unsigned long flags; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci spin_lock_irqsave(&hga_reg_lock, flags); 18462306a36Sopenharmony_ci outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_BLINK_EN, HGA_MODE_PORT); 18562306a36Sopenharmony_ci outb_p(0x00, HGA_GFX_PORT); 18662306a36Sopenharmony_ci outb_p(0x00, HGA_STATUS_PORT); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci write_hga_b(0x61, 0x00); /* horizontal total */ 18962306a36Sopenharmony_ci write_hga_b(0x50, 0x01); /* horizontal displayed */ 19062306a36Sopenharmony_ci write_hga_b(0x52, 0x02); /* horizontal sync pos */ 19162306a36Sopenharmony_ci write_hga_b(0x0f, 0x03); /* horizontal sync width */ 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci write_hga_b(0x19, 0x04); /* vertical total */ 19462306a36Sopenharmony_ci write_hga_b(0x06, 0x05); /* vertical total adjust */ 19562306a36Sopenharmony_ci write_hga_b(0x19, 0x06); /* vertical displayed */ 19662306a36Sopenharmony_ci write_hga_b(0x19, 0x07); /* vertical sync pos */ 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci write_hga_b(0x02, 0x08); /* interlace mode */ 19962306a36Sopenharmony_ci write_hga_b(0x0d, 0x09); /* maximum scanline */ 20062306a36Sopenharmony_ci write_hga_b(0x0c, 0x0a); /* cursor start */ 20162306a36Sopenharmony_ci write_hga_b(0x0d, 0x0b); /* cursor end */ 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci write_hga_w(0x0000, 0x0c); /* start address */ 20462306a36Sopenharmony_ci write_hga_w(0x0000, 0x0e); /* cursor location */ 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci hga_mode = HGA_TXT; 20762306a36Sopenharmony_ci spin_unlock_irqrestore(&hga_reg_lock, flags); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic void hga_gfx_mode(void) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci unsigned long flags; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci spin_lock_irqsave(&hga_reg_lock, flags); 21562306a36Sopenharmony_ci outb_p(0x00, HGA_STATUS_PORT); 21662306a36Sopenharmony_ci outb_p(HGA_GFX_MODE_EN, HGA_GFX_PORT); 21762306a36Sopenharmony_ci outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci write_hga_b(0x35, 0x00); /* horizontal total */ 22062306a36Sopenharmony_ci write_hga_b(0x2d, 0x01); /* horizontal displayed */ 22162306a36Sopenharmony_ci write_hga_b(0x2e, 0x02); /* horizontal sync pos */ 22262306a36Sopenharmony_ci write_hga_b(0x07, 0x03); /* horizontal sync width */ 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci write_hga_b(0x5b, 0x04); /* vertical total */ 22562306a36Sopenharmony_ci write_hga_b(0x02, 0x05); /* vertical total adjust */ 22662306a36Sopenharmony_ci write_hga_b(0x57, 0x06); /* vertical displayed */ 22762306a36Sopenharmony_ci write_hga_b(0x57, 0x07); /* vertical sync pos */ 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci write_hga_b(0x02, 0x08); /* interlace mode */ 23062306a36Sopenharmony_ci write_hga_b(0x03, 0x09); /* maximum scanline */ 23162306a36Sopenharmony_ci write_hga_b(0x00, 0x0a); /* cursor start */ 23262306a36Sopenharmony_ci write_hga_b(0x00, 0x0b); /* cursor end */ 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci write_hga_w(0x0000, 0x0c); /* start address */ 23562306a36Sopenharmony_ci write_hga_w(0x0000, 0x0e); /* cursor location */ 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci hga_mode = HGA_GFX; 23862306a36Sopenharmony_ci spin_unlock_irqrestore(&hga_reg_lock, flags); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic void hga_show_logo(struct fb_info *info) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci/* 24462306a36Sopenharmony_ci void __iomem *dest = hga_vram; 24562306a36Sopenharmony_ci char *logo = linux_logo_bw; 24662306a36Sopenharmony_ci int x, y; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci for (y = 134; y < 134 + 80 ; y++) * this needs some cleanup * 24962306a36Sopenharmony_ci for (x = 0; x < 10 ; x++) 25062306a36Sopenharmony_ci writeb(~*(logo++),(dest + HGA_ROWADDR(y) + x + 40)); 25162306a36Sopenharmony_ci*/ 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic void hga_pan(unsigned int xoffset, unsigned int yoffset) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci unsigned int base; 25762306a36Sopenharmony_ci unsigned long flags; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci base = (yoffset / 8) * 90 + xoffset; 26062306a36Sopenharmony_ci spin_lock_irqsave(&hga_reg_lock, flags); 26162306a36Sopenharmony_ci write_hga_w(base, 0x0c); /* start address */ 26262306a36Sopenharmony_ci spin_unlock_irqrestore(&hga_reg_lock, flags); 26362306a36Sopenharmony_ci DPRINTK("hga_pan: base:%d\n", base); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic void hga_blank(int blank_mode) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci unsigned long flags; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci spin_lock_irqsave(&hga_reg_lock, flags); 27162306a36Sopenharmony_ci if (blank_mode) { 27262306a36Sopenharmony_ci outb_p(0x00, HGA_MODE_PORT); /* disable video */ 27362306a36Sopenharmony_ci } else { 27462306a36Sopenharmony_ci outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT); 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci spin_unlock_irqrestore(&hga_reg_lock, flags); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int hga_card_detect(void) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci int count = 0; 28262306a36Sopenharmony_ci void __iomem *p, *q; 28362306a36Sopenharmony_ci unsigned short p_save, q_save; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci hga_vram_len = 0x08000; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci hga_vram = ioremap(0xb0000, hga_vram_len); 28862306a36Sopenharmony_ci if (!hga_vram) 28962306a36Sopenharmony_ci return -ENOMEM; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (request_region(0x3b0, 12, "hgafb")) 29262306a36Sopenharmony_ci release_io_ports = 1; 29362306a36Sopenharmony_ci if (request_region(0x3bf, 1, "hgafb")) 29462306a36Sopenharmony_ci release_io_port = 1; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* do a memory check */ 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci p = hga_vram; 29962306a36Sopenharmony_ci q = hga_vram + 0x01000; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci p_save = readw(p); q_save = readw(q); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci writew(0xaa55, p); if (readw(p) == 0xaa55) count++; 30462306a36Sopenharmony_ci writew(0x55aa, p); if (readw(p) == 0x55aa) count++; 30562306a36Sopenharmony_ci writew(p_save, p); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (count != 2) 30862306a36Sopenharmony_ci goto error; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Ok, there is definitely a card registering at the correct 31162306a36Sopenharmony_ci * memory location, so now we do an I/O port test. 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (!test_hga_b(0x66, 0x0f)) /* cursor low register */ 31562306a36Sopenharmony_ci goto error; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (!test_hga_b(0x99, 0x0f)) /* cursor low register */ 31862306a36Sopenharmony_ci goto error; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* See if the card is a Hercules, by checking whether the vsync 32162306a36Sopenharmony_ci * bit of the status register is changing. This test lasts for 32262306a36Sopenharmony_ci * approximately 1/10th of a second. 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci p_save = q_save = inb_p(HGA_STATUS_PORT) & HGA_STATUS_VSYNC; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci for (count=0; count < 50000 && p_save == q_save; count++) { 32862306a36Sopenharmony_ci q_save = inb(HGA_STATUS_PORT) & HGA_STATUS_VSYNC; 32962306a36Sopenharmony_ci udelay(2); 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (p_save == q_save) 33362306a36Sopenharmony_ci goto error; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci switch (inb_p(HGA_STATUS_PORT) & 0x70) { 33662306a36Sopenharmony_ci case 0x10: 33762306a36Sopenharmony_ci hga_type = TYPE_HERCPLUS; 33862306a36Sopenharmony_ci hga_type_name = "HerculesPlus"; 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci case 0x50: 34162306a36Sopenharmony_ci hga_type = TYPE_HERCCOLOR; 34262306a36Sopenharmony_ci hga_type_name = "HerculesColor"; 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci default: 34562306a36Sopenharmony_ci hga_type = TYPE_HERC; 34662306a36Sopenharmony_ci hga_type_name = "Hercules"; 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_cierror: 35162306a36Sopenharmony_ci if (release_io_ports) 35262306a36Sopenharmony_ci release_region(0x3b0, 12); 35362306a36Sopenharmony_ci if (release_io_port) 35462306a36Sopenharmony_ci release_region(0x3bf, 1); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci iounmap(hga_vram); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci pr_err("hgafb: HGA card not detected.\n"); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return -EINVAL; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/** 36462306a36Sopenharmony_ci * hgafb_open - open the framebuffer device 36562306a36Sopenharmony_ci * @info: pointer to fb_info object containing info for current hga board 36662306a36Sopenharmony_ci * @init: open by console system or userland. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int hgafb_open(struct fb_info *info, int init) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci hga_gfx_mode(); 37262306a36Sopenharmony_ci hga_clear_screen(); 37362306a36Sopenharmony_ci if (!nologo) hga_show_logo(info); 37462306a36Sopenharmony_ci return 0; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci/** 37862306a36Sopenharmony_ci * hgafb_release - open the framebuffer device 37962306a36Sopenharmony_ci * @info: pointer to fb_info object containing info for current hga board 38062306a36Sopenharmony_ci * @init: open by console system or userland. 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int hgafb_release(struct fb_info *info, int init) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci hga_txt_mode(); 38662306a36Sopenharmony_ci hga_clear_screen(); 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci/** 39162306a36Sopenharmony_ci * hgafb_setcolreg - set color registers 39262306a36Sopenharmony_ci * @regno:register index to set 39362306a36Sopenharmony_ci * @red:red value, unused 39462306a36Sopenharmony_ci * @green:green value, unused 39562306a36Sopenharmony_ci * @blue:blue value, unused 39662306a36Sopenharmony_ci * @transp:transparency value, unused 39762306a36Sopenharmony_ci * @info:unused 39862306a36Sopenharmony_ci * 39962306a36Sopenharmony_ci * This callback function is used to set the color registers of a HGA 40062306a36Sopenharmony_ci * board. Since we have only two fixed colors only @regno is checked. 40162306a36Sopenharmony_ci * A zero is returned on success and 1 for failure. 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic int hgafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 40562306a36Sopenharmony_ci u_int transp, struct fb_info *info) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci if (regno > 1) 40862306a36Sopenharmony_ci return 1; 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci/** 41362306a36Sopenharmony_ci * hga_pan_display - pan or wrap the display 41462306a36Sopenharmony_ci * @var:contains new xoffset, yoffset and vmode values 41562306a36Sopenharmony_ci * @info:pointer to fb_info object containing info for current hga board 41662306a36Sopenharmony_ci * 41762306a36Sopenharmony_ci * This function looks only at xoffset, yoffset and the %FB_VMODE_YWRAP 41862306a36Sopenharmony_ci * flag in @var. If input parameters are correct it calls hga_pan() to 41962306a36Sopenharmony_ci * program the hardware. @info->var is updated to the new values. 42062306a36Sopenharmony_ci * A zero is returned on success and %-EINVAL for failure. 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic int hgafb_pan_display(struct fb_var_screeninfo *var, 42462306a36Sopenharmony_ci struct fb_info *info) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci if (var->vmode & FB_VMODE_YWRAP) { 42762306a36Sopenharmony_ci if (var->yoffset >= info->var.yres_virtual || 42862306a36Sopenharmony_ci var->xoffset) 42962306a36Sopenharmony_ci return -EINVAL; 43062306a36Sopenharmony_ci } else { 43162306a36Sopenharmony_ci if (var->xoffset + info->var.xres > info->var.xres_virtual 43262306a36Sopenharmony_ci || var->yoffset + info->var.yres > info->var.yres_virtual 43362306a36Sopenharmony_ci || var->yoffset % 8) 43462306a36Sopenharmony_ci return -EINVAL; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci hga_pan(var->xoffset, var->yoffset); 43862306a36Sopenharmony_ci return 0; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci/** 44262306a36Sopenharmony_ci * hgafb_blank - (un)blank the screen 44362306a36Sopenharmony_ci * @blank_mode:blanking method to use 44462306a36Sopenharmony_ci * @info:unused 44562306a36Sopenharmony_ci * 44662306a36Sopenharmony_ci * Blank the screen if blank_mode != 0, else unblank. 44762306a36Sopenharmony_ci * Implements VESA suspend and powerdown modes on hardware that supports 44862306a36Sopenharmony_ci * disabling hsync/vsync: 44962306a36Sopenharmony_ci * @blank_mode == 2 means suspend vsync, 45062306a36Sopenharmony_ci * @blank_mode == 3 means suspend hsync, 45162306a36Sopenharmony_ci * @blank_mode == 4 means powerdown. 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int hgafb_blank(int blank_mode, struct fb_info *info) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci hga_blank(blank_mode); 45762306a36Sopenharmony_ci return 0; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci/* 46162306a36Sopenharmony_ci * Accel functions 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_cistatic void hgafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci u_int rows, y; 46662306a36Sopenharmony_ci u8 __iomem *dest; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci y = rect->dy; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci for (rows = rect->height; rows--; y++) { 47162306a36Sopenharmony_ci dest = rowaddr(info, y) + (rect->dx >> 3); 47262306a36Sopenharmony_ci switch (rect->rop) { 47362306a36Sopenharmony_ci case ROP_COPY: 47462306a36Sopenharmony_ci memset_io(dest, rect->color, (rect->width >> 3)); 47562306a36Sopenharmony_ci break; 47662306a36Sopenharmony_ci case ROP_XOR: 47762306a36Sopenharmony_ci fb_writeb(~(fb_readb(dest)), dest); 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic void hgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci u_int rows, y1, y2; 48662306a36Sopenharmony_ci u8 __iomem *src; 48762306a36Sopenharmony_ci u8 __iomem *dest; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (area->dy <= area->sy) { 49062306a36Sopenharmony_ci y1 = area->sy; 49162306a36Sopenharmony_ci y2 = area->dy; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci for (rows = area->height; rows--; ) { 49462306a36Sopenharmony_ci src = rowaddr(info, y1) + (area->sx >> 3); 49562306a36Sopenharmony_ci dest = rowaddr(info, y2) + (area->dx >> 3); 49662306a36Sopenharmony_ci memmove(dest, src, (area->width >> 3)); 49762306a36Sopenharmony_ci y1++; 49862306a36Sopenharmony_ci y2++; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci } else { 50162306a36Sopenharmony_ci y1 = area->sy + area->height - 1; 50262306a36Sopenharmony_ci y2 = area->dy + area->height - 1; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci for (rows = area->height; rows--;) { 50562306a36Sopenharmony_ci src = rowaddr(info, y1) + (area->sx >> 3); 50662306a36Sopenharmony_ci dest = rowaddr(info, y2) + (area->dx >> 3); 50762306a36Sopenharmony_ci memmove(dest, src, (area->width >> 3)); 50862306a36Sopenharmony_ci y1--; 50962306a36Sopenharmony_ci y2--; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic void hgafb_imageblit(struct fb_info *info, const struct fb_image *image) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci u8 __iomem *dest; 51762306a36Sopenharmony_ci u8 *cdat = (u8 *) image->data; 51862306a36Sopenharmony_ci u_int rows, y = image->dy; 51962306a36Sopenharmony_ci u_int x; 52062306a36Sopenharmony_ci u8 d; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci for (rows = image->height; rows--; y++) { 52362306a36Sopenharmony_ci for (x = 0; x < image->width; x+= 8) { 52462306a36Sopenharmony_ci d = *cdat++; 52562306a36Sopenharmony_ci dest = rowaddr(info, y) + ((image->dx + x)>> 3); 52662306a36Sopenharmony_ci fb_writeb(d, dest); 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic const struct fb_ops hgafb_ops = { 53262306a36Sopenharmony_ci .owner = THIS_MODULE, 53362306a36Sopenharmony_ci .fb_open = hgafb_open, 53462306a36Sopenharmony_ci .fb_release = hgafb_release, 53562306a36Sopenharmony_ci .fb_setcolreg = hgafb_setcolreg, 53662306a36Sopenharmony_ci .fb_pan_display = hgafb_pan_display, 53762306a36Sopenharmony_ci .fb_blank = hgafb_blank, 53862306a36Sopenharmony_ci .fb_fillrect = hgafb_fillrect, 53962306a36Sopenharmony_ci .fb_copyarea = hgafb_copyarea, 54062306a36Sopenharmony_ci .fb_imageblit = hgafb_imageblit, 54162306a36Sopenharmony_ci}; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci/* ------------------------------------------------------------------------- * 54462306a36Sopenharmony_ci * 54562306a36Sopenharmony_ci * Functions in fb_info 54662306a36Sopenharmony_ci * 54762306a36Sopenharmony_ci * ------------------------------------------------------------------------- */ 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */ 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* 55262306a36Sopenharmony_ci * Initialization 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic int hgafb_probe(struct platform_device *pdev) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci struct fb_info *info; 55862306a36Sopenharmony_ci int ret; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci ret = hga_card_detect(); 56162306a36Sopenharmony_ci if (ret) 56262306a36Sopenharmony_ci return ret; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci printk(KERN_INFO "hgafb: %s with %ldK of memory detected.\n", 56562306a36Sopenharmony_ci hga_type_name, hga_vram_len/1024); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci info = framebuffer_alloc(0, &pdev->dev); 56862306a36Sopenharmony_ci if (!info) { 56962306a36Sopenharmony_ci iounmap(hga_vram); 57062306a36Sopenharmony_ci return -ENOMEM; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci hga_fix.smem_start = (unsigned long)hga_vram; 57462306a36Sopenharmony_ci hga_fix.smem_len = hga_vram_len; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci info->flags = FBINFO_HWACCEL_YPAN; 57762306a36Sopenharmony_ci info->var = hga_default_var; 57862306a36Sopenharmony_ci info->fix = hga_fix; 57962306a36Sopenharmony_ci info->monspecs.hfmin = 0; 58062306a36Sopenharmony_ci info->monspecs.hfmax = 0; 58162306a36Sopenharmony_ci info->monspecs.vfmin = 10000; 58262306a36Sopenharmony_ci info->monspecs.vfmax = 10000; 58362306a36Sopenharmony_ci info->monspecs.dpms = 0; 58462306a36Sopenharmony_ci info->fbops = &hgafb_ops; 58562306a36Sopenharmony_ci info->screen_base = hga_vram; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (register_framebuffer(info) < 0) { 58862306a36Sopenharmony_ci framebuffer_release(info); 58962306a36Sopenharmony_ci iounmap(hga_vram); 59062306a36Sopenharmony_ci return -EINVAL; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci fb_info(info, "%s frame buffer device\n", info->fix.id); 59462306a36Sopenharmony_ci platform_set_drvdata(pdev, info); 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic void hgafb_remove(struct platform_device *pdev) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct fb_info *info = platform_get_drvdata(pdev); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci hga_txt_mode(); 60362306a36Sopenharmony_ci hga_clear_screen(); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (info) { 60662306a36Sopenharmony_ci unregister_framebuffer(info); 60762306a36Sopenharmony_ci framebuffer_release(info); 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci iounmap(hga_vram); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (release_io_ports) 61362306a36Sopenharmony_ci release_region(0x3b0, 12); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (release_io_port) 61662306a36Sopenharmony_ci release_region(0x3bf, 1); 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic struct platform_driver hgafb_driver = { 62062306a36Sopenharmony_ci .probe = hgafb_probe, 62162306a36Sopenharmony_ci .remove_new = hgafb_remove, 62262306a36Sopenharmony_ci .driver = { 62362306a36Sopenharmony_ci .name = "hgafb", 62462306a36Sopenharmony_ci }, 62562306a36Sopenharmony_ci}; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic struct platform_device *hgafb_device; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic int __init hgafb_init(void) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci int ret; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (fb_get_options("hgafb", NULL)) 63462306a36Sopenharmony_ci return -ENODEV; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci ret = platform_driver_register(&hgafb_driver); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (!ret) { 63962306a36Sopenharmony_ci hgafb_device = platform_device_register_simple("hgafb", 0, NULL, 0); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (IS_ERR(hgafb_device)) { 64262306a36Sopenharmony_ci platform_driver_unregister(&hgafb_driver); 64362306a36Sopenharmony_ci ret = PTR_ERR(hgafb_device); 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci return ret; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic void __exit hgafb_exit(void) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci platform_device_unregister(hgafb_device); 65362306a36Sopenharmony_ci platform_driver_unregister(&hgafb_driver); 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci/* ------------------------------------------------------------------------- 65762306a36Sopenharmony_ci * 65862306a36Sopenharmony_ci * Modularization 65962306a36Sopenharmony_ci * 66062306a36Sopenharmony_ci * ------------------------------------------------------------------------- */ 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ciMODULE_AUTHOR("Ferenc Bakonyi (fero@drama.obuda.kando.hu)"); 66362306a36Sopenharmony_ciMODULE_DESCRIPTION("FBDev driver for Hercules Graphics Adaptor"); 66462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cimodule_param(nologo, bool, 0); 66762306a36Sopenharmony_ciMODULE_PARM_DESC(nologo, "Disables startup logo if != 0 (default=0)"); 66862306a36Sopenharmony_cimodule_init(hgafb_init); 66962306a36Sopenharmony_cimodule_exit(hgafb_exit); 670