18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * linux/drivers/video/hgafb.c -- Hercules graphics adaptor frame buffer device 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Created 25 Nov 1999 by Ferenc Bakonyi (fero@drama.obuda.kando.hu) 58c2ecf20Sopenharmony_ci * Based on skeletonfb.c by Geert Uytterhoeven and 68c2ecf20Sopenharmony_ci * mdacon.c by Andrew Apted 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * History: 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * - Revision 0.1.8 (23 Oct 2002): Ported to new framebuffer api. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * - Revision 0.1.7 (23 Jan 2001): fix crash resulting from MDA only cards 138c2ecf20Sopenharmony_ci * being detected as Hercules. (Paul G.) 148c2ecf20Sopenharmony_ci * - Revision 0.1.6 (17 Aug 2000): new style structs 158c2ecf20Sopenharmony_ci * documentation 168c2ecf20Sopenharmony_ci * - Revision 0.1.5 (13 Mar 2000): spinlocks instead of saveflags();cli();etc 178c2ecf20Sopenharmony_ci * minor fixes 188c2ecf20Sopenharmony_ci * - Revision 0.1.4 (24 Jan 2000): fixed a bug in hga_card_detect() for 198c2ecf20Sopenharmony_ci * HGA-only systems 208c2ecf20Sopenharmony_ci * - Revision 0.1.3 (22 Jan 2000): modified for the new fb_info structure 218c2ecf20Sopenharmony_ci * screen is cleared after rmmod 228c2ecf20Sopenharmony_ci * virtual resolutions 238c2ecf20Sopenharmony_ci * module parameter 'nologo={0|1}' 248c2ecf20Sopenharmony_ci * the most important: boot logo :) 258c2ecf20Sopenharmony_ci * - Revision 0.1.0 (6 Dec 1999): faster scrolling and minor fixes 268c2ecf20Sopenharmony_ci * - First release (25 Nov 1999) 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 298c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive 308c2ecf20Sopenharmony_ci * for more details. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <linux/module.h> 348c2ecf20Sopenharmony_ci#include <linux/kernel.h> 358c2ecf20Sopenharmony_ci#include <linux/errno.h> 368c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 378c2ecf20Sopenharmony_ci#include <linux/string.h> 388c2ecf20Sopenharmony_ci#include <linux/mm.h> 398c2ecf20Sopenharmony_ci#include <linux/delay.h> 408c2ecf20Sopenharmony_ci#include <linux/fb.h> 418c2ecf20Sopenharmony_ci#include <linux/init.h> 428c2ecf20Sopenharmony_ci#include <linux/ioport.h> 438c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 448c2ecf20Sopenharmony_ci#include <asm/io.h> 458c2ecf20Sopenharmony_ci#include <asm/vga.h> 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#if 0 488c2ecf20Sopenharmony_ci#define DPRINTK(args...) printk(KERN_DEBUG __FILE__": " ##args) 498c2ecf20Sopenharmony_ci#else 508c2ecf20Sopenharmony_ci#define DPRINTK(args...) 518c2ecf20Sopenharmony_ci#endif 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#if 0 548c2ecf20Sopenharmony_ci#define CHKINFO(ret) if (info != &fb_info) { printk(KERN_DEBUG __FILE__": This should never happen, line:%d \n", __LINE__); return ret; } 558c2ecf20Sopenharmony_ci#else 568c2ecf20Sopenharmony_ci#define CHKINFO(ret) 578c2ecf20Sopenharmony_ci#endif 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* Description of the hardware layout */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic void __iomem *hga_vram; /* Base of video memory */ 628c2ecf20Sopenharmony_cistatic unsigned long hga_vram_len; /* Size of video memory */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define HGA_ROWADDR(row) ((row%4)*8192 + (row>>2)*90) 658c2ecf20Sopenharmony_ci#define HGA_TXT 0 668c2ecf20Sopenharmony_ci#define HGA_GFX 1 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic inline u8 __iomem * rowaddr(struct fb_info *info, u_int row) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci return info->screen_base + HGA_ROWADDR(row); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int hga_mode = -1; /* 0 = txt, 1 = gfx mode */ 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic enum { TYPE_HERC, TYPE_HERCPLUS, TYPE_HERCCOLOR } hga_type; 768c2ecf20Sopenharmony_cistatic char *hga_type_name; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define HGA_INDEX_PORT 0x3b4 /* Register select port */ 798c2ecf20Sopenharmony_ci#define HGA_VALUE_PORT 0x3b5 /* Register value port */ 808c2ecf20Sopenharmony_ci#define HGA_MODE_PORT 0x3b8 /* Mode control port */ 818c2ecf20Sopenharmony_ci#define HGA_STATUS_PORT 0x3ba /* Status and Config port */ 828c2ecf20Sopenharmony_ci#define HGA_GFX_PORT 0x3bf /* Graphics control port */ 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* HGA register values */ 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define HGA_CURSOR_BLINKING 0x00 878c2ecf20Sopenharmony_ci#define HGA_CURSOR_OFF 0x20 888c2ecf20Sopenharmony_ci#define HGA_CURSOR_SLOWBLINK 0x60 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define HGA_MODE_GRAPHICS 0x02 918c2ecf20Sopenharmony_ci#define HGA_MODE_VIDEO_EN 0x08 928c2ecf20Sopenharmony_ci#define HGA_MODE_BLINK_EN 0x20 938c2ecf20Sopenharmony_ci#define HGA_MODE_GFX_PAGE1 0x80 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define HGA_STATUS_HSYNC 0x01 968c2ecf20Sopenharmony_ci#define HGA_STATUS_VSYNC 0x80 978c2ecf20Sopenharmony_ci#define HGA_STATUS_VIDEO 0x08 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define HGA_CONFIG_COL132 0x08 1008c2ecf20Sopenharmony_ci#define HGA_GFX_MODE_EN 0x01 1018c2ecf20Sopenharmony_ci#define HGA_GFX_PAGE_EN 0x02 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* Global locks */ 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(hga_reg_lock); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* Framebuffer driver structures */ 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic const struct fb_var_screeninfo hga_default_var = { 1108c2ecf20Sopenharmony_ci .xres = 720, 1118c2ecf20Sopenharmony_ci .yres = 348, 1128c2ecf20Sopenharmony_ci .xres_virtual = 720, 1138c2ecf20Sopenharmony_ci .yres_virtual = 348, 1148c2ecf20Sopenharmony_ci .bits_per_pixel = 1, 1158c2ecf20Sopenharmony_ci .red = {0, 1, 0}, 1168c2ecf20Sopenharmony_ci .green = {0, 1, 0}, 1178c2ecf20Sopenharmony_ci .blue = {0, 1, 0}, 1188c2ecf20Sopenharmony_ci .transp = {0, 0, 0}, 1198c2ecf20Sopenharmony_ci .height = -1, 1208c2ecf20Sopenharmony_ci .width = -1, 1218c2ecf20Sopenharmony_ci}; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic struct fb_fix_screeninfo hga_fix = { 1248c2ecf20Sopenharmony_ci .id = "HGA", 1258c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, /* (not sure) */ 1268c2ecf20Sopenharmony_ci .visual = FB_VISUAL_MONO10, 1278c2ecf20Sopenharmony_ci .xpanstep = 8, 1288c2ecf20Sopenharmony_ci .ypanstep = 8, 1298c2ecf20Sopenharmony_ci .line_length = 90, 1308c2ecf20Sopenharmony_ci .accel = FB_ACCEL_NONE 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* Don't assume that tty1 will be the initial current console. */ 1348c2ecf20Sopenharmony_cistatic int release_io_port = 0; 1358c2ecf20Sopenharmony_cistatic int release_io_ports = 0; 1368c2ecf20Sopenharmony_cistatic bool nologo = 0; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- 1398c2ecf20Sopenharmony_ci * 1408c2ecf20Sopenharmony_ci * Low level hardware functions 1418c2ecf20Sopenharmony_ci * 1428c2ecf20Sopenharmony_ci * ------------------------------------------------------------------------- */ 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic void write_hga_b(unsigned int val, unsigned char reg) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci outb_p(reg, HGA_INDEX_PORT); 1478c2ecf20Sopenharmony_ci outb_p(val, HGA_VALUE_PORT); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void write_hga_w(unsigned int val, unsigned char reg) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci outb_p(reg, HGA_INDEX_PORT); outb_p(val >> 8, HGA_VALUE_PORT); 1538c2ecf20Sopenharmony_ci outb_p(reg+1, HGA_INDEX_PORT); outb_p(val & 0xff, HGA_VALUE_PORT); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int test_hga_b(unsigned char val, unsigned char reg) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci outb_p(reg, HGA_INDEX_PORT); 1598c2ecf20Sopenharmony_ci outb (val, HGA_VALUE_PORT); 1608c2ecf20Sopenharmony_ci udelay(20); val = (inb_p(HGA_VALUE_PORT) == val); 1618c2ecf20Sopenharmony_ci return val; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void hga_clear_screen(void) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci unsigned char fillchar = 0xbf; /* magic */ 1678c2ecf20Sopenharmony_ci unsigned long flags; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci spin_lock_irqsave(&hga_reg_lock, flags); 1708c2ecf20Sopenharmony_ci if (hga_mode == HGA_TXT) 1718c2ecf20Sopenharmony_ci fillchar = ' '; 1728c2ecf20Sopenharmony_ci else if (hga_mode == HGA_GFX) 1738c2ecf20Sopenharmony_ci fillchar = 0x00; 1748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hga_reg_lock, flags); 1758c2ecf20Sopenharmony_ci if (fillchar != 0xbf) 1768c2ecf20Sopenharmony_ci memset_io(hga_vram, fillchar, hga_vram_len); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void hga_txt_mode(void) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci unsigned long flags; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci spin_lock_irqsave(&hga_reg_lock, flags); 1848c2ecf20Sopenharmony_ci outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_BLINK_EN, HGA_MODE_PORT); 1858c2ecf20Sopenharmony_ci outb_p(0x00, HGA_GFX_PORT); 1868c2ecf20Sopenharmony_ci outb_p(0x00, HGA_STATUS_PORT); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci write_hga_b(0x61, 0x00); /* horizontal total */ 1898c2ecf20Sopenharmony_ci write_hga_b(0x50, 0x01); /* horizontal displayed */ 1908c2ecf20Sopenharmony_ci write_hga_b(0x52, 0x02); /* horizontal sync pos */ 1918c2ecf20Sopenharmony_ci write_hga_b(0x0f, 0x03); /* horizontal sync width */ 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci write_hga_b(0x19, 0x04); /* vertical total */ 1948c2ecf20Sopenharmony_ci write_hga_b(0x06, 0x05); /* vertical total adjust */ 1958c2ecf20Sopenharmony_ci write_hga_b(0x19, 0x06); /* vertical displayed */ 1968c2ecf20Sopenharmony_ci write_hga_b(0x19, 0x07); /* vertical sync pos */ 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci write_hga_b(0x02, 0x08); /* interlace mode */ 1998c2ecf20Sopenharmony_ci write_hga_b(0x0d, 0x09); /* maximum scanline */ 2008c2ecf20Sopenharmony_ci write_hga_b(0x0c, 0x0a); /* cursor start */ 2018c2ecf20Sopenharmony_ci write_hga_b(0x0d, 0x0b); /* cursor end */ 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci write_hga_w(0x0000, 0x0c); /* start address */ 2048c2ecf20Sopenharmony_ci write_hga_w(0x0000, 0x0e); /* cursor location */ 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci hga_mode = HGA_TXT; 2078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hga_reg_lock, flags); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic void hga_gfx_mode(void) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci unsigned long flags; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci spin_lock_irqsave(&hga_reg_lock, flags); 2158c2ecf20Sopenharmony_ci outb_p(0x00, HGA_STATUS_PORT); 2168c2ecf20Sopenharmony_ci outb_p(HGA_GFX_MODE_EN, HGA_GFX_PORT); 2178c2ecf20Sopenharmony_ci outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci write_hga_b(0x35, 0x00); /* horizontal total */ 2208c2ecf20Sopenharmony_ci write_hga_b(0x2d, 0x01); /* horizontal displayed */ 2218c2ecf20Sopenharmony_ci write_hga_b(0x2e, 0x02); /* horizontal sync pos */ 2228c2ecf20Sopenharmony_ci write_hga_b(0x07, 0x03); /* horizontal sync width */ 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci write_hga_b(0x5b, 0x04); /* vertical total */ 2258c2ecf20Sopenharmony_ci write_hga_b(0x02, 0x05); /* vertical total adjust */ 2268c2ecf20Sopenharmony_ci write_hga_b(0x57, 0x06); /* vertical displayed */ 2278c2ecf20Sopenharmony_ci write_hga_b(0x57, 0x07); /* vertical sync pos */ 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci write_hga_b(0x02, 0x08); /* interlace mode */ 2308c2ecf20Sopenharmony_ci write_hga_b(0x03, 0x09); /* maximum scanline */ 2318c2ecf20Sopenharmony_ci write_hga_b(0x00, 0x0a); /* cursor start */ 2328c2ecf20Sopenharmony_ci write_hga_b(0x00, 0x0b); /* cursor end */ 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci write_hga_w(0x0000, 0x0c); /* start address */ 2358c2ecf20Sopenharmony_ci write_hga_w(0x0000, 0x0e); /* cursor location */ 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci hga_mode = HGA_GFX; 2388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hga_reg_lock, flags); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic void hga_show_logo(struct fb_info *info) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci/* 2448c2ecf20Sopenharmony_ci void __iomem *dest = hga_vram; 2458c2ecf20Sopenharmony_ci char *logo = linux_logo_bw; 2468c2ecf20Sopenharmony_ci int x, y; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci for (y = 134; y < 134 + 80 ; y++) * this needs some cleanup * 2498c2ecf20Sopenharmony_ci for (x = 0; x < 10 ; x++) 2508c2ecf20Sopenharmony_ci writeb(~*(logo++),(dest + HGA_ROWADDR(y) + x + 40)); 2518c2ecf20Sopenharmony_ci*/ 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic void hga_pan(unsigned int xoffset, unsigned int yoffset) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci unsigned int base; 2578c2ecf20Sopenharmony_ci unsigned long flags; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci base = (yoffset / 8) * 90 + xoffset; 2608c2ecf20Sopenharmony_ci spin_lock_irqsave(&hga_reg_lock, flags); 2618c2ecf20Sopenharmony_ci write_hga_w(base, 0x0c); /* start address */ 2628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hga_reg_lock, flags); 2638c2ecf20Sopenharmony_ci DPRINTK("hga_pan: base:%d\n", base); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic void hga_blank(int blank_mode) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci unsigned long flags; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci spin_lock_irqsave(&hga_reg_lock, flags); 2718c2ecf20Sopenharmony_ci if (blank_mode) { 2728c2ecf20Sopenharmony_ci outb_p(0x00, HGA_MODE_PORT); /* disable video */ 2738c2ecf20Sopenharmony_ci } else { 2748c2ecf20Sopenharmony_ci outb_p(HGA_MODE_VIDEO_EN | HGA_MODE_GRAPHICS, HGA_MODE_PORT); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hga_reg_lock, flags); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int hga_card_detect(void) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci int count = 0; 2828c2ecf20Sopenharmony_ci void __iomem *p, *q; 2838c2ecf20Sopenharmony_ci unsigned short p_save, q_save; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci hga_vram_len = 0x08000; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci hga_vram = ioremap(0xb0000, hga_vram_len); 2888c2ecf20Sopenharmony_ci if (!hga_vram) 2898c2ecf20Sopenharmony_ci return -ENOMEM; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (request_region(0x3b0, 12, "hgafb")) 2928c2ecf20Sopenharmony_ci release_io_ports = 1; 2938c2ecf20Sopenharmony_ci if (request_region(0x3bf, 1, "hgafb")) 2948c2ecf20Sopenharmony_ci release_io_port = 1; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* do a memory check */ 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci p = hga_vram; 2998c2ecf20Sopenharmony_ci q = hga_vram + 0x01000; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci p_save = readw(p); q_save = readw(q); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci writew(0xaa55, p); if (readw(p) == 0xaa55) count++; 3048c2ecf20Sopenharmony_ci writew(0x55aa, p); if (readw(p) == 0x55aa) count++; 3058c2ecf20Sopenharmony_ci writew(p_save, p); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (count != 2) 3088c2ecf20Sopenharmony_ci goto error; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* Ok, there is definitely a card registering at the correct 3118c2ecf20Sopenharmony_ci * memory location, so now we do an I/O port test. 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (!test_hga_b(0x66, 0x0f)) /* cursor low register */ 3158c2ecf20Sopenharmony_ci goto error; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (!test_hga_b(0x99, 0x0f)) /* cursor low register */ 3188c2ecf20Sopenharmony_ci goto error; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* See if the card is a Hercules, by checking whether the vsync 3218c2ecf20Sopenharmony_ci * bit of the status register is changing. This test lasts for 3228c2ecf20Sopenharmony_ci * approximately 1/10th of a second. 3238c2ecf20Sopenharmony_ci */ 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci p_save = q_save = inb_p(HGA_STATUS_PORT) & HGA_STATUS_VSYNC; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci for (count=0; count < 50000 && p_save == q_save; count++) { 3288c2ecf20Sopenharmony_ci q_save = inb(HGA_STATUS_PORT) & HGA_STATUS_VSYNC; 3298c2ecf20Sopenharmony_ci udelay(2); 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (p_save == q_save) 3338c2ecf20Sopenharmony_ci goto error; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci switch (inb_p(HGA_STATUS_PORT) & 0x70) { 3368c2ecf20Sopenharmony_ci case 0x10: 3378c2ecf20Sopenharmony_ci hga_type = TYPE_HERCPLUS; 3388c2ecf20Sopenharmony_ci hga_type_name = "HerculesPlus"; 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci case 0x50: 3418c2ecf20Sopenharmony_ci hga_type = TYPE_HERCCOLOR; 3428c2ecf20Sopenharmony_ci hga_type_name = "HerculesColor"; 3438c2ecf20Sopenharmony_ci break; 3448c2ecf20Sopenharmony_ci default: 3458c2ecf20Sopenharmony_ci hga_type = TYPE_HERC; 3468c2ecf20Sopenharmony_ci hga_type_name = "Hercules"; 3478c2ecf20Sopenharmony_ci break; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_cierror: 3518c2ecf20Sopenharmony_ci if (release_io_ports) 3528c2ecf20Sopenharmony_ci release_region(0x3b0, 12); 3538c2ecf20Sopenharmony_ci if (release_io_port) 3548c2ecf20Sopenharmony_ci release_region(0x3bf, 1); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci iounmap(hga_vram); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci pr_err("hgafb: HGA card not detected.\n"); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return -EINVAL; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci/** 3648c2ecf20Sopenharmony_ci * hgafb_open - open the framebuffer device 3658c2ecf20Sopenharmony_ci * @info:pointer to fb_info object containing info for current hga board 3668c2ecf20Sopenharmony_ci * @int:open by console system or userland. 3678c2ecf20Sopenharmony_ci */ 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic int hgafb_open(struct fb_info *info, int init) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci hga_gfx_mode(); 3728c2ecf20Sopenharmony_ci hga_clear_screen(); 3738c2ecf20Sopenharmony_ci if (!nologo) hga_show_logo(info); 3748c2ecf20Sopenharmony_ci return 0; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/** 3788c2ecf20Sopenharmony_ci * hgafb_open - open the framebuffer device 3798c2ecf20Sopenharmony_ci * @info:pointer to fb_info object containing info for current hga board 3808c2ecf20Sopenharmony_ci * @int:open by console system or userland. 3818c2ecf20Sopenharmony_ci */ 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int hgafb_release(struct fb_info *info, int init) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci hga_txt_mode(); 3868c2ecf20Sopenharmony_ci hga_clear_screen(); 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci/** 3918c2ecf20Sopenharmony_ci * hgafb_setcolreg - set color registers 3928c2ecf20Sopenharmony_ci * @regno:register index to set 3938c2ecf20Sopenharmony_ci * @red:red value, unused 3948c2ecf20Sopenharmony_ci * @green:green value, unused 3958c2ecf20Sopenharmony_ci * @blue:blue value, unused 3968c2ecf20Sopenharmony_ci * @transp:transparency value, unused 3978c2ecf20Sopenharmony_ci * @info:unused 3988c2ecf20Sopenharmony_ci * 3998c2ecf20Sopenharmony_ci * This callback function is used to set the color registers of a HGA 4008c2ecf20Sopenharmony_ci * board. Since we have only two fixed colors only @regno is checked. 4018c2ecf20Sopenharmony_ci * A zero is returned on success and 1 for failure. 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic int hgafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 4058c2ecf20Sopenharmony_ci u_int transp, struct fb_info *info) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci if (regno > 1) 4088c2ecf20Sopenharmony_ci return 1; 4098c2ecf20Sopenharmony_ci return 0; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci/** 4138c2ecf20Sopenharmony_ci * hga_pan_display - pan or wrap the display 4148c2ecf20Sopenharmony_ci * @var:contains new xoffset, yoffset and vmode values 4158c2ecf20Sopenharmony_ci * @info:pointer to fb_info object containing info for current hga board 4168c2ecf20Sopenharmony_ci * 4178c2ecf20Sopenharmony_ci * This function looks only at xoffset, yoffset and the %FB_VMODE_YWRAP 4188c2ecf20Sopenharmony_ci * flag in @var. If input parameters are correct it calls hga_pan() to 4198c2ecf20Sopenharmony_ci * program the hardware. @info->var is updated to the new values. 4208c2ecf20Sopenharmony_ci * A zero is returned on success and %-EINVAL for failure. 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int hgafb_pan_display(struct fb_var_screeninfo *var, 4248c2ecf20Sopenharmony_ci struct fb_info *info) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci if (var->vmode & FB_VMODE_YWRAP) { 4278c2ecf20Sopenharmony_ci if (var->yoffset >= info->var.yres_virtual || 4288c2ecf20Sopenharmony_ci var->xoffset) 4298c2ecf20Sopenharmony_ci return -EINVAL; 4308c2ecf20Sopenharmony_ci } else { 4318c2ecf20Sopenharmony_ci if (var->xoffset + info->var.xres > info->var.xres_virtual 4328c2ecf20Sopenharmony_ci || var->yoffset + info->var.yres > info->var.yres_virtual 4338c2ecf20Sopenharmony_ci || var->yoffset % 8) 4348c2ecf20Sopenharmony_ci return -EINVAL; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci hga_pan(var->xoffset, var->yoffset); 4388c2ecf20Sopenharmony_ci return 0; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/** 4428c2ecf20Sopenharmony_ci * hgafb_blank - (un)blank the screen 4438c2ecf20Sopenharmony_ci * @blank_mode:blanking method to use 4448c2ecf20Sopenharmony_ci * @info:unused 4458c2ecf20Sopenharmony_ci * 4468c2ecf20Sopenharmony_ci * Blank the screen if blank_mode != 0, else unblank. 4478c2ecf20Sopenharmony_ci * Implements VESA suspend and powerdown modes on hardware that supports 4488c2ecf20Sopenharmony_ci * disabling hsync/vsync: 4498c2ecf20Sopenharmony_ci * @blank_mode == 2 means suspend vsync, 4508c2ecf20Sopenharmony_ci * @blank_mode == 3 means suspend hsync, 4518c2ecf20Sopenharmony_ci * @blank_mode == 4 means powerdown. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic int hgafb_blank(int blank_mode, struct fb_info *info) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci hga_blank(blank_mode); 4578c2ecf20Sopenharmony_ci return 0; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci/* 4618c2ecf20Sopenharmony_ci * Accel functions 4628c2ecf20Sopenharmony_ci */ 4638c2ecf20Sopenharmony_cistatic void hgafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci u_int rows, y; 4668c2ecf20Sopenharmony_ci u8 __iomem *dest; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci y = rect->dy; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci for (rows = rect->height; rows--; y++) { 4718c2ecf20Sopenharmony_ci dest = rowaddr(info, y) + (rect->dx >> 3); 4728c2ecf20Sopenharmony_ci switch (rect->rop) { 4738c2ecf20Sopenharmony_ci case ROP_COPY: 4748c2ecf20Sopenharmony_ci memset_io(dest, rect->color, (rect->width >> 3)); 4758c2ecf20Sopenharmony_ci break; 4768c2ecf20Sopenharmony_ci case ROP_XOR: 4778c2ecf20Sopenharmony_ci fb_writeb(~(fb_readb(dest)), dest); 4788c2ecf20Sopenharmony_ci break; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic void hgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci u_int rows, y1, y2; 4868c2ecf20Sopenharmony_ci u8 __iomem *src; 4878c2ecf20Sopenharmony_ci u8 __iomem *dest; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (area->dy <= area->sy) { 4908c2ecf20Sopenharmony_ci y1 = area->sy; 4918c2ecf20Sopenharmony_ci y2 = area->dy; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci for (rows = area->height; rows--; ) { 4948c2ecf20Sopenharmony_ci src = rowaddr(info, y1) + (area->sx >> 3); 4958c2ecf20Sopenharmony_ci dest = rowaddr(info, y2) + (area->dx >> 3); 4968c2ecf20Sopenharmony_ci memmove(dest, src, (area->width >> 3)); 4978c2ecf20Sopenharmony_ci y1++; 4988c2ecf20Sopenharmony_ci y2++; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci } else { 5018c2ecf20Sopenharmony_ci y1 = area->sy + area->height - 1; 5028c2ecf20Sopenharmony_ci y2 = area->dy + area->height - 1; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci for (rows = area->height; rows--;) { 5058c2ecf20Sopenharmony_ci src = rowaddr(info, y1) + (area->sx >> 3); 5068c2ecf20Sopenharmony_ci dest = rowaddr(info, y2) + (area->dx >> 3); 5078c2ecf20Sopenharmony_ci memmove(dest, src, (area->width >> 3)); 5088c2ecf20Sopenharmony_ci y1--; 5098c2ecf20Sopenharmony_ci y2--; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic void hgafb_imageblit(struct fb_info *info, const struct fb_image *image) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci u8 __iomem *dest; 5178c2ecf20Sopenharmony_ci u8 *cdat = (u8 *) image->data; 5188c2ecf20Sopenharmony_ci u_int rows, y = image->dy; 5198c2ecf20Sopenharmony_ci u_int x; 5208c2ecf20Sopenharmony_ci u8 d; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci for (rows = image->height; rows--; y++) { 5238c2ecf20Sopenharmony_ci for (x = 0; x < image->width; x+= 8) { 5248c2ecf20Sopenharmony_ci d = *cdat++; 5258c2ecf20Sopenharmony_ci dest = rowaddr(info, y) + ((image->dx + x)>> 3); 5268c2ecf20Sopenharmony_ci fb_writeb(d, dest); 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic const struct fb_ops hgafb_ops = { 5328c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5338c2ecf20Sopenharmony_ci .fb_open = hgafb_open, 5348c2ecf20Sopenharmony_ci .fb_release = hgafb_release, 5358c2ecf20Sopenharmony_ci .fb_setcolreg = hgafb_setcolreg, 5368c2ecf20Sopenharmony_ci .fb_pan_display = hgafb_pan_display, 5378c2ecf20Sopenharmony_ci .fb_blank = hgafb_blank, 5388c2ecf20Sopenharmony_ci .fb_fillrect = hgafb_fillrect, 5398c2ecf20Sopenharmony_ci .fb_copyarea = hgafb_copyarea, 5408c2ecf20Sopenharmony_ci .fb_imageblit = hgafb_imageblit, 5418c2ecf20Sopenharmony_ci}; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- * 5448c2ecf20Sopenharmony_ci * 5458c2ecf20Sopenharmony_ci * Functions in fb_info 5468c2ecf20Sopenharmony_ci * 5478c2ecf20Sopenharmony_ci * ------------------------------------------------------------------------- */ 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* 5528c2ecf20Sopenharmony_ci * Initialization 5538c2ecf20Sopenharmony_ci */ 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic int hgafb_probe(struct platform_device *pdev) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct fb_info *info; 5588c2ecf20Sopenharmony_ci int ret; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci ret = hga_card_detect(); 5618c2ecf20Sopenharmony_ci if (ret) 5628c2ecf20Sopenharmony_ci return ret; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci printk(KERN_INFO "hgafb: %s with %ldK of memory detected.\n", 5658c2ecf20Sopenharmony_ci hga_type_name, hga_vram_len/1024); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci info = framebuffer_alloc(0, &pdev->dev); 5688c2ecf20Sopenharmony_ci if (!info) { 5698c2ecf20Sopenharmony_ci iounmap(hga_vram); 5708c2ecf20Sopenharmony_ci return -ENOMEM; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci hga_fix.smem_start = (unsigned long)hga_vram; 5748c2ecf20Sopenharmony_ci hga_fix.smem_len = hga_vram_len; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; 5778c2ecf20Sopenharmony_ci info->var = hga_default_var; 5788c2ecf20Sopenharmony_ci info->fix = hga_fix; 5798c2ecf20Sopenharmony_ci info->monspecs.hfmin = 0; 5808c2ecf20Sopenharmony_ci info->monspecs.hfmax = 0; 5818c2ecf20Sopenharmony_ci info->monspecs.vfmin = 10000; 5828c2ecf20Sopenharmony_ci info->monspecs.vfmax = 10000; 5838c2ecf20Sopenharmony_ci info->monspecs.dpms = 0; 5848c2ecf20Sopenharmony_ci info->fbops = &hgafb_ops; 5858c2ecf20Sopenharmony_ci info->screen_base = hga_vram; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (register_framebuffer(info) < 0) { 5888c2ecf20Sopenharmony_ci framebuffer_release(info); 5898c2ecf20Sopenharmony_ci iounmap(hga_vram); 5908c2ecf20Sopenharmony_ci return -EINVAL; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci fb_info(info, "%s frame buffer device\n", info->fix.id); 5948c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, info); 5958c2ecf20Sopenharmony_ci return 0; 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic int hgafb_remove(struct platform_device *pdev) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct fb_info *info = platform_get_drvdata(pdev); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci hga_txt_mode(); 6038c2ecf20Sopenharmony_ci hga_clear_screen(); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (info) { 6068c2ecf20Sopenharmony_ci unregister_framebuffer(info); 6078c2ecf20Sopenharmony_ci framebuffer_release(info); 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci iounmap(hga_vram); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (release_io_ports) 6138c2ecf20Sopenharmony_ci release_region(0x3b0, 12); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (release_io_port) 6168c2ecf20Sopenharmony_ci release_region(0x3bf, 1); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci return 0; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic struct platform_driver hgafb_driver = { 6228c2ecf20Sopenharmony_ci .probe = hgafb_probe, 6238c2ecf20Sopenharmony_ci .remove = hgafb_remove, 6248c2ecf20Sopenharmony_ci .driver = { 6258c2ecf20Sopenharmony_ci .name = "hgafb", 6268c2ecf20Sopenharmony_ci }, 6278c2ecf20Sopenharmony_ci}; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic struct platform_device *hgafb_device; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic int __init hgafb_init(void) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci int ret; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (fb_get_options("hgafb", NULL)) 6368c2ecf20Sopenharmony_ci return -ENODEV; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci ret = platform_driver_register(&hgafb_driver); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (!ret) { 6418c2ecf20Sopenharmony_ci hgafb_device = platform_device_register_simple("hgafb", 0, NULL, 0); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (IS_ERR(hgafb_device)) { 6448c2ecf20Sopenharmony_ci platform_driver_unregister(&hgafb_driver); 6458c2ecf20Sopenharmony_ci ret = PTR_ERR(hgafb_device); 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci return ret; 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic void __exit hgafb_exit(void) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci platform_device_unregister(hgafb_device); 6558c2ecf20Sopenharmony_ci platform_driver_unregister(&hgafb_driver); 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- 6598c2ecf20Sopenharmony_ci * 6608c2ecf20Sopenharmony_ci * Modularization 6618c2ecf20Sopenharmony_ci * 6628c2ecf20Sopenharmony_ci * ------------------------------------------------------------------------- */ 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ferenc Bakonyi (fero@drama.obuda.kando.hu)"); 6658c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("FBDev driver for Hercules Graphics Adaptor"); 6668c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cimodule_param(nologo, bool, 0); 6698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nologo, "Disables startup logo if != 0 (default=0)"); 6708c2ecf20Sopenharmony_cimodule_init(hgafb_init); 6718c2ecf20Sopenharmony_cimodule_exit(hgafb_exit); 672