18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * framebuffer driver for VBE 2.0 compliant graphic boards 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * switching to graphics mode happens at boot time (while 68c2ecf20Sopenharmony_ci * running in real mode, see arch/i386/boot/video.S). 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/string.h> 168c2ecf20Sopenharmony_ci#include <linux/mm.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/fb.h> 198c2ecf20Sopenharmony_ci#include <linux/ioport.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/screen_info.h> 238c2ecf20Sopenharmony_ci#include <linux/io.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <video/vga.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define dac_reg (0x3c8) 288c2ecf20Sopenharmony_ci#define dac_val (0x3c9) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct vesafb_par { 338c2ecf20Sopenharmony_ci u32 pseudo_palette[256]; 348c2ecf20Sopenharmony_ci int wc_cookie; 358c2ecf20Sopenharmony_ci struct resource *region; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic struct fb_var_screeninfo vesafb_defined = { 398c2ecf20Sopenharmony_ci .activate = FB_ACTIVATE_NOW, 408c2ecf20Sopenharmony_ci .height = -1, 418c2ecf20Sopenharmony_ci .width = -1, 428c2ecf20Sopenharmony_ci .right_margin = 32, 438c2ecf20Sopenharmony_ci .upper_margin = 16, 448c2ecf20Sopenharmony_ci .lower_margin = 4, 458c2ecf20Sopenharmony_ci .vsync_len = 4, 468c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic struct fb_fix_screeninfo vesafb_fix = { 508c2ecf20Sopenharmony_ci .id = "VESA VGA", 518c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 528c2ecf20Sopenharmony_ci .accel = FB_ACCEL_NONE, 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int inverse __read_mostly; 568c2ecf20Sopenharmony_cistatic int mtrr __read_mostly; /* disable mtrr */ 578c2ecf20Sopenharmony_cistatic int vram_remap; /* Set amount of memory to be used */ 588c2ecf20Sopenharmony_cistatic int vram_total; /* Set total amount of memory */ 598c2ecf20Sopenharmony_cistatic int pmi_setpal __read_mostly = 1; /* pmi for palette changes ??? */ 608c2ecf20Sopenharmony_cistatic int ypan __read_mostly; /* 0..nothing, 1..ypan, 2..ywrap */ 618c2ecf20Sopenharmony_cistatic void (*pmi_start)(void) __read_mostly; 628c2ecf20Sopenharmony_cistatic void (*pmi_pal) (void) __read_mostly; 638c2ecf20Sopenharmony_cistatic int depth __read_mostly; 648c2ecf20Sopenharmony_cistatic int vga_compat __read_mostly; 658c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- */ 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int vesafb_pan_display(struct fb_var_screeninfo *var, 688c2ecf20Sopenharmony_ci struct fb_info *info) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci#ifdef __i386__ 718c2ecf20Sopenharmony_ci int offset; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci offset = (var->yoffset * info->fix.line_length + var->xoffset) / 4; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci __asm__ __volatile__( 768c2ecf20Sopenharmony_ci "call *(%%edi)" 778c2ecf20Sopenharmony_ci : /* no return value */ 788c2ecf20Sopenharmony_ci : "a" (0x4f07), /* EAX */ 798c2ecf20Sopenharmony_ci "b" (0), /* EBX */ 808c2ecf20Sopenharmony_ci "c" (offset), /* ECX */ 818c2ecf20Sopenharmony_ci "d" (offset >> 16), /* EDX */ 828c2ecf20Sopenharmony_ci "D" (&pmi_start)); /* EDI */ 838c2ecf20Sopenharmony_ci#endif 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int vesa_setpalette(int regno, unsigned red, unsigned green, 888c2ecf20Sopenharmony_ci unsigned blue) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci int shift = 16 - depth; 918c2ecf20Sopenharmony_ci int err = -EINVAL; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* 948c2ecf20Sopenharmony_ci * Try VGA registers first... 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci if (vga_compat) { 978c2ecf20Sopenharmony_ci outb_p(regno, dac_reg); 988c2ecf20Sopenharmony_ci outb_p(red >> shift, dac_val); 998c2ecf20Sopenharmony_ci outb_p(green >> shift, dac_val); 1008c2ecf20Sopenharmony_ci outb_p(blue >> shift, dac_val); 1018c2ecf20Sopenharmony_ci err = 0; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#ifdef __i386__ 1058c2ecf20Sopenharmony_ci/* 1068c2ecf20Sopenharmony_ci * Fallback to the PMI.... 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_ci if (err && pmi_setpal) { 1098c2ecf20Sopenharmony_ci struct { u_char blue, green, red, pad; } entry; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci entry.red = red >> shift; 1128c2ecf20Sopenharmony_ci entry.green = green >> shift; 1138c2ecf20Sopenharmony_ci entry.blue = blue >> shift; 1148c2ecf20Sopenharmony_ci entry.pad = 0; 1158c2ecf20Sopenharmony_ci __asm__ __volatile__( 1168c2ecf20Sopenharmony_ci "call *(%%esi)" 1178c2ecf20Sopenharmony_ci : /* no return value */ 1188c2ecf20Sopenharmony_ci : "a" (0x4f09), /* EAX */ 1198c2ecf20Sopenharmony_ci "b" (0), /* EBX */ 1208c2ecf20Sopenharmony_ci "c" (1), /* ECX */ 1218c2ecf20Sopenharmony_ci "d" (regno), /* EDX */ 1228c2ecf20Sopenharmony_ci "D" (&entry), /* EDI */ 1238c2ecf20Sopenharmony_ci "S" (&pmi_pal)); /* ESI */ 1248c2ecf20Sopenharmony_ci err = 0; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci#endif 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return err; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int vesafb_setcolreg(unsigned regno, unsigned red, unsigned green, 1328c2ecf20Sopenharmony_ci unsigned blue, unsigned transp, 1338c2ecf20Sopenharmony_ci struct fb_info *info) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci int err = 0; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * Set a single color register. The values supplied are 1398c2ecf20Sopenharmony_ci * already rounded down to the hardware's capabilities 1408c2ecf20Sopenharmony_ci * (according to the entries in the `var' structure). Return 1418c2ecf20Sopenharmony_ci * != 0 for invalid regno. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (regno >= info->cmap.len) 1458c2ecf20Sopenharmony_ci return 1; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (info->var.bits_per_pixel == 8) 1488c2ecf20Sopenharmony_ci err = vesa_setpalette(regno,red,green,blue); 1498c2ecf20Sopenharmony_ci else if (regno < 16) { 1508c2ecf20Sopenharmony_ci switch (info->var.bits_per_pixel) { 1518c2ecf20Sopenharmony_ci case 16: 1528c2ecf20Sopenharmony_ci if (info->var.red.offset == 10) { 1538c2ecf20Sopenharmony_ci /* 1:5:5:5 */ 1548c2ecf20Sopenharmony_ci ((u32*) (info->pseudo_palette))[regno] = 1558c2ecf20Sopenharmony_ci ((red & 0xf800) >> 1) | 1568c2ecf20Sopenharmony_ci ((green & 0xf800) >> 6) | 1578c2ecf20Sopenharmony_ci ((blue & 0xf800) >> 11); 1588c2ecf20Sopenharmony_ci } else { 1598c2ecf20Sopenharmony_ci /* 0:5:6:5 */ 1608c2ecf20Sopenharmony_ci ((u32*) (info->pseudo_palette))[regno] = 1618c2ecf20Sopenharmony_ci ((red & 0xf800) ) | 1628c2ecf20Sopenharmony_ci ((green & 0xfc00) >> 5) | 1638c2ecf20Sopenharmony_ci ((blue & 0xf800) >> 11); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci case 24: 1678c2ecf20Sopenharmony_ci case 32: 1688c2ecf20Sopenharmony_ci red >>= 8; 1698c2ecf20Sopenharmony_ci green >>= 8; 1708c2ecf20Sopenharmony_ci blue >>= 8; 1718c2ecf20Sopenharmony_ci ((u32 *)(info->pseudo_palette))[regno] = 1728c2ecf20Sopenharmony_ci (red << info->var.red.offset) | 1738c2ecf20Sopenharmony_ci (green << info->var.green.offset) | 1748c2ecf20Sopenharmony_ci (blue << info->var.blue.offset); 1758c2ecf20Sopenharmony_ci break; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return err; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void vesafb_destroy(struct fb_info *info) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct vesafb_par *par = info->par; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 1878c2ecf20Sopenharmony_ci arch_phys_wc_del(par->wc_cookie); 1888c2ecf20Sopenharmony_ci if (info->screen_base) 1898c2ecf20Sopenharmony_ci iounmap(info->screen_base); 1908c2ecf20Sopenharmony_ci release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic struct fb_ops vesafb_ops = { 1948c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1958c2ecf20Sopenharmony_ci .fb_destroy = vesafb_destroy, 1968c2ecf20Sopenharmony_ci .fb_setcolreg = vesafb_setcolreg, 1978c2ecf20Sopenharmony_ci .fb_pan_display = vesafb_pan_display, 1988c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 1998c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 2008c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 2018c2ecf20Sopenharmony_ci}; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int vesafb_setup(char *options) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci char *this_opt; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (!options || !*options) 2088c2ecf20Sopenharmony_ci return 0; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci while ((this_opt = strsep(&options, ",")) != NULL) { 2118c2ecf20Sopenharmony_ci if (!*this_opt) continue; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (! strcmp(this_opt, "inverse")) 2148c2ecf20Sopenharmony_ci inverse=1; 2158c2ecf20Sopenharmony_ci else if (! strcmp(this_opt, "redraw")) 2168c2ecf20Sopenharmony_ci ypan=0; 2178c2ecf20Sopenharmony_ci else if (! strcmp(this_opt, "ypan")) 2188c2ecf20Sopenharmony_ci ypan=1; 2198c2ecf20Sopenharmony_ci else if (! strcmp(this_opt, "ywrap")) 2208c2ecf20Sopenharmony_ci ypan=2; 2218c2ecf20Sopenharmony_ci else if (! strcmp(this_opt, "vgapal")) 2228c2ecf20Sopenharmony_ci pmi_setpal=0; 2238c2ecf20Sopenharmony_ci else if (! strcmp(this_opt, "pmipal")) 2248c2ecf20Sopenharmony_ci pmi_setpal=1; 2258c2ecf20Sopenharmony_ci else if (! strncmp(this_opt, "mtrr:", 5)) 2268c2ecf20Sopenharmony_ci mtrr = simple_strtoul(this_opt+5, NULL, 0); 2278c2ecf20Sopenharmony_ci else if (! strcmp(this_opt, "nomtrr")) 2288c2ecf20Sopenharmony_ci mtrr=0; 2298c2ecf20Sopenharmony_ci else if (! strncmp(this_opt, "vtotal:", 7)) 2308c2ecf20Sopenharmony_ci vram_total = simple_strtoul(this_opt+7, NULL, 0); 2318c2ecf20Sopenharmony_ci else if (! strncmp(this_opt, "vremap:", 7)) 2328c2ecf20Sopenharmony_ci vram_remap = simple_strtoul(this_opt+7, NULL, 0); 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int vesafb_probe(struct platform_device *dev) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct fb_info *info; 2408c2ecf20Sopenharmony_ci struct vesafb_par *par; 2418c2ecf20Sopenharmony_ci int i, err; 2428c2ecf20Sopenharmony_ci unsigned int size_vmode; 2438c2ecf20Sopenharmony_ci unsigned int size_remap; 2448c2ecf20Sopenharmony_ci unsigned int size_total; 2458c2ecf20Sopenharmony_ci char *option = NULL; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* ignore error return of fb_get_options */ 2488c2ecf20Sopenharmony_ci fb_get_options("vesafb", &option); 2498c2ecf20Sopenharmony_ci vesafb_setup(option); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (screen_info.orig_video_isVGA != VIDEO_TYPE_VLFB) 2528c2ecf20Sopenharmony_ci return -ENODEV; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci vga_compat = (screen_info.capabilities & 2) ? 0 : 1; 2558c2ecf20Sopenharmony_ci vesafb_fix.smem_start = screen_info.lfb_base; 2568c2ecf20Sopenharmony_ci vesafb_defined.bits_per_pixel = screen_info.lfb_depth; 2578c2ecf20Sopenharmony_ci if (15 == vesafb_defined.bits_per_pixel) 2588c2ecf20Sopenharmony_ci vesafb_defined.bits_per_pixel = 16; 2598c2ecf20Sopenharmony_ci vesafb_defined.xres = screen_info.lfb_width; 2608c2ecf20Sopenharmony_ci vesafb_defined.yres = screen_info.lfb_height; 2618c2ecf20Sopenharmony_ci vesafb_fix.line_length = screen_info.lfb_linelength; 2628c2ecf20Sopenharmony_ci vesafb_fix.visual = (vesafb_defined.bits_per_pixel == 8) ? 2638c2ecf20Sopenharmony_ci FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* size_vmode -- that is the amount of memory needed for the 2668c2ecf20Sopenharmony_ci * used video mode, i.e. the minimum amount of 2678c2ecf20Sopenharmony_ci * memory we need. */ 2688c2ecf20Sopenharmony_ci size_vmode = vesafb_defined.yres * vesafb_fix.line_length; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* size_total -- all video memory we have. Used for mtrr 2718c2ecf20Sopenharmony_ci * entries, resource allocation and bounds 2728c2ecf20Sopenharmony_ci * checking. */ 2738c2ecf20Sopenharmony_ci size_total = screen_info.lfb_size * 65536; 2748c2ecf20Sopenharmony_ci if (vram_total) 2758c2ecf20Sopenharmony_ci size_total = vram_total * 1024 * 1024; 2768c2ecf20Sopenharmony_ci if (size_total < size_vmode) 2778c2ecf20Sopenharmony_ci size_total = size_vmode; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* size_remap -- the amount of video memory we are going to 2808c2ecf20Sopenharmony_ci * use for vesafb. With modern cards it is no 2818c2ecf20Sopenharmony_ci * option to simply use size_total as that 2828c2ecf20Sopenharmony_ci * wastes plenty of kernel address space. */ 2838c2ecf20Sopenharmony_ci size_remap = size_vmode * 2; 2848c2ecf20Sopenharmony_ci if (vram_remap) 2858c2ecf20Sopenharmony_ci size_remap = vram_remap * 1024 * 1024; 2868c2ecf20Sopenharmony_ci if (size_remap < size_vmode) 2878c2ecf20Sopenharmony_ci size_remap = size_vmode; 2888c2ecf20Sopenharmony_ci if (size_remap > size_total) 2898c2ecf20Sopenharmony_ci size_remap = size_total; 2908c2ecf20Sopenharmony_ci vesafb_fix.smem_len = size_remap; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci#ifndef __i386__ 2938c2ecf20Sopenharmony_ci screen_info.vesapm_seg = 0; 2948c2ecf20Sopenharmony_ci#endif 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (!request_mem_region(vesafb_fix.smem_start, size_total, "vesafb")) { 2978c2ecf20Sopenharmony_ci printk(KERN_WARNING 2988c2ecf20Sopenharmony_ci "vesafb: cannot reserve video memory at 0x%lx\n", 2998c2ecf20Sopenharmony_ci vesafb_fix.smem_start); 3008c2ecf20Sopenharmony_ci /* We cannot make this fatal. Sometimes this comes from magic 3018c2ecf20Sopenharmony_ci spaces our resource handlers simply don't know about */ 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct vesafb_par), &dev->dev); 3058c2ecf20Sopenharmony_ci if (!info) { 3068c2ecf20Sopenharmony_ci release_mem_region(vesafb_fix.smem_start, size_total); 3078c2ecf20Sopenharmony_ci return -ENOMEM; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci platform_set_drvdata(dev, info); 3108c2ecf20Sopenharmony_ci par = info->par; 3118c2ecf20Sopenharmony_ci info->pseudo_palette = par->pseudo_palette; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* set vesafb aperture size for generic probing */ 3148c2ecf20Sopenharmony_ci info->apertures = alloc_apertures(1); 3158c2ecf20Sopenharmony_ci if (!info->apertures) { 3168c2ecf20Sopenharmony_ci err = -ENOMEM; 3178c2ecf20Sopenharmony_ci goto err; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci info->apertures->ranges[0].base = screen_info.lfb_base; 3208c2ecf20Sopenharmony_ci info->apertures->ranges[0].size = size_total; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci printk(KERN_INFO "vesafb: mode is %dx%dx%d, linelength=%d, pages=%d\n", 3238c2ecf20Sopenharmony_ci vesafb_defined.xres, vesafb_defined.yres, vesafb_defined.bits_per_pixel, vesafb_fix.line_length, screen_info.pages); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (screen_info.vesapm_seg) { 3268c2ecf20Sopenharmony_ci printk(KERN_INFO "vesafb: protected mode interface info at %04x:%04x\n", 3278c2ecf20Sopenharmony_ci screen_info.vesapm_seg,screen_info.vesapm_off); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (screen_info.vesapm_seg < 0xc000) 3318c2ecf20Sopenharmony_ci ypan = pmi_setpal = 0; /* not available or some DOS TSR ... */ 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (ypan || pmi_setpal) { 3348c2ecf20Sopenharmony_ci unsigned short *pmi_base; 3358c2ecf20Sopenharmony_ci pmi_base = (unsigned short*)phys_to_virt(((unsigned long)screen_info.vesapm_seg << 4) + screen_info.vesapm_off); 3368c2ecf20Sopenharmony_ci pmi_start = (void*)((char*)pmi_base + pmi_base[1]); 3378c2ecf20Sopenharmony_ci pmi_pal = (void*)((char*)pmi_base + pmi_base[2]); 3388c2ecf20Sopenharmony_ci printk(KERN_INFO "vesafb: pmi: set display start = %p, set palette = %p\n",pmi_start,pmi_pal); 3398c2ecf20Sopenharmony_ci if (pmi_base[3]) { 3408c2ecf20Sopenharmony_ci printk(KERN_INFO "vesafb: pmi: ports = "); 3418c2ecf20Sopenharmony_ci for (i = pmi_base[3]/2; pmi_base[i] != 0xffff; i++) 3428c2ecf20Sopenharmony_ci printk("%x ", pmi_base[i]); 3438c2ecf20Sopenharmony_ci printk("\n"); 3448c2ecf20Sopenharmony_ci if (pmi_base[i] != 0xffff) { 3458c2ecf20Sopenharmony_ci /* 3468c2ecf20Sopenharmony_ci * memory areas not supported (yet?) 3478c2ecf20Sopenharmony_ci * 3488c2ecf20Sopenharmony_ci * Rules are: we have to set up a descriptor for the requested 3498c2ecf20Sopenharmony_ci * memory area and pass it in the ES register to the BIOS function. 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_ci printk(KERN_INFO "vesafb: can't handle memory requests, pmi disabled\n"); 3528c2ecf20Sopenharmony_ci ypan = pmi_setpal = 0; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (vesafb_defined.bits_per_pixel == 8 && !pmi_setpal && !vga_compat) { 3588c2ecf20Sopenharmony_ci printk(KERN_WARNING "vesafb: hardware palette is unchangeable,\n" 3598c2ecf20Sopenharmony_ci " colors may be incorrect\n"); 3608c2ecf20Sopenharmony_ci vesafb_fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci vesafb_defined.xres_virtual = vesafb_defined.xres; 3648c2ecf20Sopenharmony_ci vesafb_defined.yres_virtual = vesafb_fix.smem_len / vesafb_fix.line_length; 3658c2ecf20Sopenharmony_ci if (ypan && vesafb_defined.yres_virtual > vesafb_defined.yres) { 3668c2ecf20Sopenharmony_ci printk(KERN_INFO "vesafb: scrolling: %s using protected mode interface, yres_virtual=%d\n", 3678c2ecf20Sopenharmony_ci (ypan > 1) ? "ywrap" : "ypan",vesafb_defined.yres_virtual); 3688c2ecf20Sopenharmony_ci } else { 3698c2ecf20Sopenharmony_ci printk(KERN_INFO "vesafb: scrolling: redraw\n"); 3708c2ecf20Sopenharmony_ci vesafb_defined.yres_virtual = vesafb_defined.yres; 3718c2ecf20Sopenharmony_ci ypan = 0; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* some dummy values for timing to make fbset happy */ 3758c2ecf20Sopenharmony_ci vesafb_defined.pixclock = 10000000 / vesafb_defined.xres * 1000 / vesafb_defined.yres; 3768c2ecf20Sopenharmony_ci vesafb_defined.left_margin = (vesafb_defined.xres / 8) & 0xf8; 3778c2ecf20Sopenharmony_ci vesafb_defined.hsync_len = (vesafb_defined.xres / 8) & 0xf8; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci vesafb_defined.red.offset = screen_info.red_pos; 3808c2ecf20Sopenharmony_ci vesafb_defined.red.length = screen_info.red_size; 3818c2ecf20Sopenharmony_ci vesafb_defined.green.offset = screen_info.green_pos; 3828c2ecf20Sopenharmony_ci vesafb_defined.green.length = screen_info.green_size; 3838c2ecf20Sopenharmony_ci vesafb_defined.blue.offset = screen_info.blue_pos; 3848c2ecf20Sopenharmony_ci vesafb_defined.blue.length = screen_info.blue_size; 3858c2ecf20Sopenharmony_ci vesafb_defined.transp.offset = screen_info.rsvd_pos; 3868c2ecf20Sopenharmony_ci vesafb_defined.transp.length = screen_info.rsvd_size; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (vesafb_defined.bits_per_pixel <= 8) { 3898c2ecf20Sopenharmony_ci depth = vesafb_defined.green.length; 3908c2ecf20Sopenharmony_ci vesafb_defined.red.length = 3918c2ecf20Sopenharmony_ci vesafb_defined.green.length = 3928c2ecf20Sopenharmony_ci vesafb_defined.blue.length = 3938c2ecf20Sopenharmony_ci vesafb_defined.bits_per_pixel; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci printk(KERN_INFO "vesafb: %s: " 3978c2ecf20Sopenharmony_ci "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n", 3988c2ecf20Sopenharmony_ci (vesafb_defined.bits_per_pixel > 8) ? 3998c2ecf20Sopenharmony_ci "Truecolor" : (vga_compat || pmi_setpal) ? 4008c2ecf20Sopenharmony_ci "Pseudocolor" : "Static Pseudocolor", 4018c2ecf20Sopenharmony_ci screen_info.rsvd_size, 4028c2ecf20Sopenharmony_ci screen_info.red_size, 4038c2ecf20Sopenharmony_ci screen_info.green_size, 4048c2ecf20Sopenharmony_ci screen_info.blue_size, 4058c2ecf20Sopenharmony_ci screen_info.rsvd_pos, 4068c2ecf20Sopenharmony_ci screen_info.red_pos, 4078c2ecf20Sopenharmony_ci screen_info.green_pos, 4088c2ecf20Sopenharmony_ci screen_info.blue_pos); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci vesafb_fix.ypanstep = ypan ? 1 : 0; 4118c2ecf20Sopenharmony_ci vesafb_fix.ywrapstep = (ypan>1) ? 1 : 0; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* request failure does not faze us, as vgacon probably has this 4148c2ecf20Sopenharmony_ci * region already (FIXME) */ 4158c2ecf20Sopenharmony_ci par->region = request_region(0x3c0, 32, "vesafb"); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (mtrr == 3) { 4188c2ecf20Sopenharmony_ci unsigned int temp_size = size_total; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* Find the largest power-of-two */ 4218c2ecf20Sopenharmony_ci temp_size = roundup_pow_of_two(temp_size); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* Try and find a power of two to add */ 4248c2ecf20Sopenharmony_ci do { 4258c2ecf20Sopenharmony_ci par->wc_cookie = 4268c2ecf20Sopenharmony_ci arch_phys_wc_add(vesafb_fix.smem_start, 4278c2ecf20Sopenharmony_ci temp_size); 4288c2ecf20Sopenharmony_ci temp_size >>= 1; 4298c2ecf20Sopenharmony_ci } while (temp_size >= PAGE_SIZE && par->wc_cookie < 0); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci info->screen_base = ioremap_wc(vesafb_fix.smem_start, vesafb_fix.smem_len); 4328c2ecf20Sopenharmony_ci } else { 4338c2ecf20Sopenharmony_ci if (mtrr && mtrr != 3) 4348c2ecf20Sopenharmony_ci WARN_ONCE(1, "Only MTRR_TYPE_WRCOMB (3) make sense\n"); 4358c2ecf20Sopenharmony_ci info->screen_base = ioremap(vesafb_fix.smem_start, vesafb_fix.smem_len); 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (!info->screen_base) { 4398c2ecf20Sopenharmony_ci printk(KERN_ERR 4408c2ecf20Sopenharmony_ci "vesafb: abort, cannot ioremap video memory 0x%x @ 0x%lx\n", 4418c2ecf20Sopenharmony_ci vesafb_fix.smem_len, vesafb_fix.smem_start); 4428c2ecf20Sopenharmony_ci err = -EIO; 4438c2ecf20Sopenharmony_ci goto err_release_region; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci printk(KERN_INFO "vesafb: framebuffer at 0x%lx, mapped to 0x%p, " 4478c2ecf20Sopenharmony_ci "using %dk, total %dk\n", 4488c2ecf20Sopenharmony_ci vesafb_fix.smem_start, info->screen_base, 4498c2ecf20Sopenharmony_ci size_remap/1024, size_total/1024); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (!ypan) 4528c2ecf20Sopenharmony_ci vesafb_ops.fb_pan_display = NULL; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci info->fbops = &vesafb_ops; 4558c2ecf20Sopenharmony_ci info->var = vesafb_defined; 4568c2ecf20Sopenharmony_ci info->fix = vesafb_fix; 4578c2ecf20Sopenharmony_ci info->flags = FBINFO_FLAG_DEFAULT | FBINFO_MISC_FIRMWARE | 4588c2ecf20Sopenharmony_ci (ypan ? FBINFO_HWACCEL_YPAN : 0); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { 4618c2ecf20Sopenharmony_ci err = -ENOMEM; 4628c2ecf20Sopenharmony_ci goto err_release_region; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci if (register_framebuffer(info)<0) { 4658c2ecf20Sopenharmony_ci err = -EINVAL; 4668c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 4678c2ecf20Sopenharmony_ci goto err_release_region; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci fb_info(info, "%s frame buffer device\n", info->fix.id); 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_cierr_release_region: 4728c2ecf20Sopenharmony_ci arch_phys_wc_del(par->wc_cookie); 4738c2ecf20Sopenharmony_ci if (info->screen_base) 4748c2ecf20Sopenharmony_ci iounmap(info->screen_base); 4758c2ecf20Sopenharmony_ci if (par->region) 4768c2ecf20Sopenharmony_ci release_region(0x3c0, 32); 4778c2ecf20Sopenharmony_cierr: 4788c2ecf20Sopenharmony_ci framebuffer_release(info); 4798c2ecf20Sopenharmony_ci release_mem_region(vesafb_fix.smem_start, size_total); 4808c2ecf20Sopenharmony_ci return err; 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic int vesafb_remove(struct platform_device *pdev) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci struct fb_info *info = platform_get_drvdata(pdev); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci unregister_framebuffer(info); 4888c2ecf20Sopenharmony_ci if (((struct vesafb_par *)(info->par))->region) 4898c2ecf20Sopenharmony_ci release_region(0x3c0, 32); 4908c2ecf20Sopenharmony_ci framebuffer_release(info); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return 0; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic struct platform_driver vesafb_driver = { 4968c2ecf20Sopenharmony_ci .driver = { 4978c2ecf20Sopenharmony_ci .name = "vesa-framebuffer", 4988c2ecf20Sopenharmony_ci }, 4998c2ecf20Sopenharmony_ci .probe = vesafb_probe, 5008c2ecf20Sopenharmony_ci .remove = vesafb_remove, 5018c2ecf20Sopenharmony_ci}; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cimodule_platform_driver(vesafb_driver); 5048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 505