18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * SGI GBE frame buffer driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 1999 Silicon Graphics, Inc. - Jeffrey Newquist 58c2ecf20Sopenharmony_ci * Copyright (C) 2002 Vivien Chappelier <vivien.chappelier@linux-mips.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 88c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 98c2ecf20Sopenharmony_ci * more details. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 158c2ecf20Sopenharmony_ci#include <linux/errno.h> 168c2ecf20Sopenharmony_ci#include <linux/gfp.h> 178c2ecf20Sopenharmony_ci#include <linux/fb.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/mm.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/io.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS 268c2ecf20Sopenharmony_ci#include <asm/addrspace.h> 278c2ecf20Sopenharmony_ci#endif 288c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 298c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <video/gbe.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic struct sgi_gbe *gbe; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct gbefb_par { 368c2ecf20Sopenharmony_ci struct fb_var_screeninfo var; 378c2ecf20Sopenharmony_ci struct gbe_timing_info timing; 388c2ecf20Sopenharmony_ci int wc_cookie; 398c2ecf20Sopenharmony_ci int valid; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define GBE_BASE 0x16000000 /* SGI O2 */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* macro for fastest write-though access to the framebuffer */ 458c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS 468c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_R10000 478c2ecf20Sopenharmony_ci#define pgprot_fb(_prot) (((_prot) & (~_CACHE_MASK)) | _CACHE_UNCACHED_ACCELERATED) 488c2ecf20Sopenharmony_ci#else 498c2ecf20Sopenharmony_ci#define pgprot_fb(_prot) (((_prot) & (~_CACHE_MASK)) | _CACHE_CACHABLE_NO_WA) 508c2ecf20Sopenharmony_ci#endif 518c2ecf20Sopenharmony_ci#endif 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * RAM we reserve for the frame buffer. This defines the maximum screen 558c2ecf20Sopenharmony_ci * size 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci#if CONFIG_FB_GBE_MEM > 8 588c2ecf20Sopenharmony_ci#error GBE Framebuffer cannot use more than 8MB of memory 598c2ecf20Sopenharmony_ci#endif 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define TILE_SHIFT 16 628c2ecf20Sopenharmony_ci#define TILE_SIZE (1 << TILE_SHIFT) 638c2ecf20Sopenharmony_ci#define TILE_MASK (TILE_SIZE - 1) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic unsigned int gbe_mem_size = CONFIG_FB_GBE_MEM * 1024*1024; 668c2ecf20Sopenharmony_cistatic void *gbe_mem; 678c2ecf20Sopenharmony_cistatic dma_addr_t gbe_dma_addr; 688c2ecf20Sopenharmony_cistatic unsigned long gbe_mem_phys; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic struct { 718c2ecf20Sopenharmony_ci uint16_t *cpu; 728c2ecf20Sopenharmony_ci dma_addr_t dma; 738c2ecf20Sopenharmony_ci} gbe_tiles; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int gbe_revision; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int ypan, ywrap; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic uint32_t pseudo_palette[16]; 808c2ecf20Sopenharmony_cistatic uint32_t gbe_cmap[256]; 818c2ecf20Sopenharmony_cistatic int gbe_turned_on; /* 0 turned off, 1 turned on */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic char *mode_option = NULL; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* default CRT mode */ 868c2ecf20Sopenharmony_cistatic struct fb_var_screeninfo default_var_CRT = { 878c2ecf20Sopenharmony_ci /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */ 888c2ecf20Sopenharmony_ci .xres = 640, 898c2ecf20Sopenharmony_ci .yres = 480, 908c2ecf20Sopenharmony_ci .xres_virtual = 640, 918c2ecf20Sopenharmony_ci .yres_virtual = 480, 928c2ecf20Sopenharmony_ci .xoffset = 0, 938c2ecf20Sopenharmony_ci .yoffset = 0, 948c2ecf20Sopenharmony_ci .bits_per_pixel = 8, 958c2ecf20Sopenharmony_ci .grayscale = 0, 968c2ecf20Sopenharmony_ci .red = { 0, 8, 0 }, 978c2ecf20Sopenharmony_ci .green = { 0, 8, 0 }, 988c2ecf20Sopenharmony_ci .blue = { 0, 8, 0 }, 998c2ecf20Sopenharmony_ci .transp = { 0, 0, 0 }, 1008c2ecf20Sopenharmony_ci .nonstd = 0, 1018c2ecf20Sopenharmony_ci .activate = 0, 1028c2ecf20Sopenharmony_ci .height = -1, 1038c2ecf20Sopenharmony_ci .width = -1, 1048c2ecf20Sopenharmony_ci .accel_flags = 0, 1058c2ecf20Sopenharmony_ci .pixclock = 39722, /* picoseconds */ 1068c2ecf20Sopenharmony_ci .left_margin = 48, 1078c2ecf20Sopenharmony_ci .right_margin = 16, 1088c2ecf20Sopenharmony_ci .upper_margin = 33, 1098c2ecf20Sopenharmony_ci .lower_margin = 10, 1108c2ecf20Sopenharmony_ci .hsync_len = 96, 1118c2ecf20Sopenharmony_ci .vsync_len = 2, 1128c2ecf20Sopenharmony_ci .sync = 0, 1138c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* default LCD mode */ 1178c2ecf20Sopenharmony_cistatic struct fb_var_screeninfo default_var_LCD = { 1188c2ecf20Sopenharmony_ci /* 1600x1024, 8 bpp */ 1198c2ecf20Sopenharmony_ci .xres = 1600, 1208c2ecf20Sopenharmony_ci .yres = 1024, 1218c2ecf20Sopenharmony_ci .xres_virtual = 1600, 1228c2ecf20Sopenharmony_ci .yres_virtual = 1024, 1238c2ecf20Sopenharmony_ci .xoffset = 0, 1248c2ecf20Sopenharmony_ci .yoffset = 0, 1258c2ecf20Sopenharmony_ci .bits_per_pixel = 8, 1268c2ecf20Sopenharmony_ci .grayscale = 0, 1278c2ecf20Sopenharmony_ci .red = { 0, 8, 0 }, 1288c2ecf20Sopenharmony_ci .green = { 0, 8, 0 }, 1298c2ecf20Sopenharmony_ci .blue = { 0, 8, 0 }, 1308c2ecf20Sopenharmony_ci .transp = { 0, 0, 0 }, 1318c2ecf20Sopenharmony_ci .nonstd = 0, 1328c2ecf20Sopenharmony_ci .activate = 0, 1338c2ecf20Sopenharmony_ci .height = -1, 1348c2ecf20Sopenharmony_ci .width = -1, 1358c2ecf20Sopenharmony_ci .accel_flags = 0, 1368c2ecf20Sopenharmony_ci .pixclock = 9353, 1378c2ecf20Sopenharmony_ci .left_margin = 20, 1388c2ecf20Sopenharmony_ci .right_margin = 30, 1398c2ecf20Sopenharmony_ci .upper_margin = 37, 1408c2ecf20Sopenharmony_ci .lower_margin = 3, 1418c2ecf20Sopenharmony_ci .hsync_len = 20, 1428c2ecf20Sopenharmony_ci .vsync_len = 3, 1438c2ecf20Sopenharmony_ci .sync = 0, 1448c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* default modedb mode */ 1488c2ecf20Sopenharmony_ci/* 640x480, 60 Hz, Non-Interlaced (25.172 MHz dotclock) */ 1498c2ecf20Sopenharmony_cistatic struct fb_videomode default_mode_CRT = { 1508c2ecf20Sopenharmony_ci .refresh = 60, 1518c2ecf20Sopenharmony_ci .xres = 640, 1528c2ecf20Sopenharmony_ci .yres = 480, 1538c2ecf20Sopenharmony_ci .pixclock = 39722, 1548c2ecf20Sopenharmony_ci .left_margin = 48, 1558c2ecf20Sopenharmony_ci .right_margin = 16, 1568c2ecf20Sopenharmony_ci .upper_margin = 33, 1578c2ecf20Sopenharmony_ci .lower_margin = 10, 1588c2ecf20Sopenharmony_ci .hsync_len = 96, 1598c2ecf20Sopenharmony_ci .vsync_len = 2, 1608c2ecf20Sopenharmony_ci .sync = 0, 1618c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci/* 1600x1024 SGI flatpanel 1600sw */ 1648c2ecf20Sopenharmony_cistatic struct fb_videomode default_mode_LCD = { 1658c2ecf20Sopenharmony_ci /* 1600x1024, 8 bpp */ 1668c2ecf20Sopenharmony_ci .xres = 1600, 1678c2ecf20Sopenharmony_ci .yres = 1024, 1688c2ecf20Sopenharmony_ci .pixclock = 9353, 1698c2ecf20Sopenharmony_ci .left_margin = 20, 1708c2ecf20Sopenharmony_ci .right_margin = 30, 1718c2ecf20Sopenharmony_ci .upper_margin = 37, 1728c2ecf20Sopenharmony_ci .lower_margin = 3, 1738c2ecf20Sopenharmony_ci .hsync_len = 20, 1748c2ecf20Sopenharmony_ci .vsync_len = 3, 1758c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic struct fb_videomode *default_mode = &default_mode_CRT; 1798c2ecf20Sopenharmony_cistatic struct fb_var_screeninfo *default_var = &default_var_CRT; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int flat_panel_enabled = 0; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void gbe_reset(void) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci /* Turn on dotclock PLL */ 1868c2ecf20Sopenharmony_ci gbe->ctrlstat = 0x300aa000; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/* 1918c2ecf20Sopenharmony_ci * Function: gbe_turn_off 1928c2ecf20Sopenharmony_ci * Parameters: (None) 1938c2ecf20Sopenharmony_ci * Description: This should turn off the monitor and gbe. This is used 1948c2ecf20Sopenharmony_ci * when switching between the serial console and the graphics 1958c2ecf20Sopenharmony_ci * console. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic void gbe_turn_off(void) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci int i; 2018c2ecf20Sopenharmony_ci unsigned int val, x, y, vpixen_off; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci gbe_turned_on = 0; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* check if pixel counter is on */ 2068c2ecf20Sopenharmony_ci val = gbe->vt_xy; 2078c2ecf20Sopenharmony_ci if (GET_GBE_FIELD(VT_XY, FREEZE, val) == 1) 2088c2ecf20Sopenharmony_ci return; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* turn off DMA */ 2118c2ecf20Sopenharmony_ci val = gbe->ovr_control; 2128c2ecf20Sopenharmony_ci SET_GBE_FIELD(OVR_CONTROL, OVR_DMA_ENABLE, val, 0); 2138c2ecf20Sopenharmony_ci gbe->ovr_control = val; 2148c2ecf20Sopenharmony_ci udelay(1000); 2158c2ecf20Sopenharmony_ci val = gbe->frm_control; 2168c2ecf20Sopenharmony_ci SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 0); 2178c2ecf20Sopenharmony_ci gbe->frm_control = val; 2188c2ecf20Sopenharmony_ci udelay(1000); 2198c2ecf20Sopenharmony_ci val = gbe->did_control; 2208c2ecf20Sopenharmony_ci SET_GBE_FIELD(DID_CONTROL, DID_DMA_ENABLE, val, 0); 2218c2ecf20Sopenharmony_ci gbe->did_control = val; 2228c2ecf20Sopenharmony_ci udelay(1000); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* We have to wait through two vertical retrace periods before 2258c2ecf20Sopenharmony_ci * the pixel DMA is turned off for sure. */ 2268c2ecf20Sopenharmony_ci for (i = 0; i < 10000; i++) { 2278c2ecf20Sopenharmony_ci val = gbe->frm_inhwctrl; 2288c2ecf20Sopenharmony_ci if (GET_GBE_FIELD(FRM_INHWCTRL, FRM_DMA_ENABLE, val)) { 2298c2ecf20Sopenharmony_ci udelay(10); 2308c2ecf20Sopenharmony_ci } else { 2318c2ecf20Sopenharmony_ci val = gbe->ovr_inhwctrl; 2328c2ecf20Sopenharmony_ci if (GET_GBE_FIELD(OVR_INHWCTRL, OVR_DMA_ENABLE, val)) { 2338c2ecf20Sopenharmony_ci udelay(10); 2348c2ecf20Sopenharmony_ci } else { 2358c2ecf20Sopenharmony_ci val = gbe->did_inhwctrl; 2368c2ecf20Sopenharmony_ci if (GET_GBE_FIELD(DID_INHWCTRL, DID_DMA_ENABLE, val)) { 2378c2ecf20Sopenharmony_ci udelay(10); 2388c2ecf20Sopenharmony_ci } else 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci if (i == 10000) 2448c2ecf20Sopenharmony_ci printk(KERN_ERR "gbefb: turn off DMA timed out\n"); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* wait for vpixen_off */ 2478c2ecf20Sopenharmony_ci val = gbe->vt_vpixen; 2488c2ecf20Sopenharmony_ci vpixen_off = GET_GBE_FIELD(VT_VPIXEN, VPIXEN_OFF, val); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci for (i = 0; i < 100000; i++) { 2518c2ecf20Sopenharmony_ci val = gbe->vt_xy; 2528c2ecf20Sopenharmony_ci x = GET_GBE_FIELD(VT_XY, X, val); 2538c2ecf20Sopenharmony_ci y = GET_GBE_FIELD(VT_XY, Y, val); 2548c2ecf20Sopenharmony_ci if (y < vpixen_off) 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci udelay(1); 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci if (i == 100000) 2598c2ecf20Sopenharmony_ci printk(KERN_ERR 2608c2ecf20Sopenharmony_ci "gbefb: wait for vpixen_off timed out\n"); 2618c2ecf20Sopenharmony_ci for (i = 0; i < 10000; i++) { 2628c2ecf20Sopenharmony_ci val = gbe->vt_xy; 2638c2ecf20Sopenharmony_ci x = GET_GBE_FIELD(VT_XY, X, val); 2648c2ecf20Sopenharmony_ci y = GET_GBE_FIELD(VT_XY, Y, val); 2658c2ecf20Sopenharmony_ci if (y > vpixen_off) 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci udelay(1); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci if (i == 10000) 2708c2ecf20Sopenharmony_ci printk(KERN_ERR "gbefb: wait for vpixen_off timed out\n"); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* turn off pixel counter */ 2738c2ecf20Sopenharmony_ci val = 0; 2748c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_XY, FREEZE, val, 1); 2758c2ecf20Sopenharmony_ci gbe->vt_xy = val; 2768c2ecf20Sopenharmony_ci mdelay(10); 2778c2ecf20Sopenharmony_ci for (i = 0; i < 10000; i++) { 2788c2ecf20Sopenharmony_ci val = gbe->vt_xy; 2798c2ecf20Sopenharmony_ci if (GET_GBE_FIELD(VT_XY, FREEZE, val) != 1) 2808c2ecf20Sopenharmony_ci udelay(10); 2818c2ecf20Sopenharmony_ci else 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci if (i == 10000) 2858c2ecf20Sopenharmony_ci printk(KERN_ERR "gbefb: turn off pixel clock timed out\n"); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* turn off dot clock */ 2888c2ecf20Sopenharmony_ci val = gbe->dotclock; 2898c2ecf20Sopenharmony_ci SET_GBE_FIELD(DOTCLK, RUN, val, 0); 2908c2ecf20Sopenharmony_ci gbe->dotclock = val; 2918c2ecf20Sopenharmony_ci mdelay(10); 2928c2ecf20Sopenharmony_ci for (i = 0; i < 10000; i++) { 2938c2ecf20Sopenharmony_ci val = gbe->dotclock; 2948c2ecf20Sopenharmony_ci if (GET_GBE_FIELD(DOTCLK, RUN, val)) 2958c2ecf20Sopenharmony_ci udelay(10); 2968c2ecf20Sopenharmony_ci else 2978c2ecf20Sopenharmony_ci break; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci if (i == 10000) 3008c2ecf20Sopenharmony_ci printk(KERN_ERR "gbefb: turn off dotclock timed out\n"); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* reset the frame DMA FIFO */ 3038c2ecf20Sopenharmony_ci val = gbe->frm_size_tile; 3048c2ecf20Sopenharmony_ci SET_GBE_FIELD(FRM_SIZE_TILE, FRM_FIFO_RESET, val, 1); 3058c2ecf20Sopenharmony_ci gbe->frm_size_tile = val; 3068c2ecf20Sopenharmony_ci SET_GBE_FIELD(FRM_SIZE_TILE, FRM_FIFO_RESET, val, 0); 3078c2ecf20Sopenharmony_ci gbe->frm_size_tile = val; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic void gbe_turn_on(void) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci unsigned int val, i; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* 3158c2ecf20Sopenharmony_ci * Check if pixel counter is off, for unknown reason this 3168c2ecf20Sopenharmony_ci * code hangs Visual Workstations 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_ci if (gbe_revision < 2) { 3198c2ecf20Sopenharmony_ci val = gbe->vt_xy; 3208c2ecf20Sopenharmony_ci if (GET_GBE_FIELD(VT_XY, FREEZE, val) == 0) 3218c2ecf20Sopenharmony_ci return; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* turn on dot clock */ 3258c2ecf20Sopenharmony_ci val = gbe->dotclock; 3268c2ecf20Sopenharmony_ci SET_GBE_FIELD(DOTCLK, RUN, val, 1); 3278c2ecf20Sopenharmony_ci gbe->dotclock = val; 3288c2ecf20Sopenharmony_ci mdelay(10); 3298c2ecf20Sopenharmony_ci for (i = 0; i < 10000; i++) { 3308c2ecf20Sopenharmony_ci val = gbe->dotclock; 3318c2ecf20Sopenharmony_ci if (GET_GBE_FIELD(DOTCLK, RUN, val) != 1) 3328c2ecf20Sopenharmony_ci udelay(10); 3338c2ecf20Sopenharmony_ci else 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci if (i == 10000) 3378c2ecf20Sopenharmony_ci printk(KERN_ERR "gbefb: turn on dotclock timed out\n"); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* turn on pixel counter */ 3408c2ecf20Sopenharmony_ci val = 0; 3418c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_XY, FREEZE, val, 0); 3428c2ecf20Sopenharmony_ci gbe->vt_xy = val; 3438c2ecf20Sopenharmony_ci mdelay(10); 3448c2ecf20Sopenharmony_ci for (i = 0; i < 10000; i++) { 3458c2ecf20Sopenharmony_ci val = gbe->vt_xy; 3468c2ecf20Sopenharmony_ci if (GET_GBE_FIELD(VT_XY, FREEZE, val)) 3478c2ecf20Sopenharmony_ci udelay(10); 3488c2ecf20Sopenharmony_ci else 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci if (i == 10000) 3528c2ecf20Sopenharmony_ci printk(KERN_ERR "gbefb: turn on pixel clock timed out\n"); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* turn on DMA */ 3558c2ecf20Sopenharmony_ci val = gbe->frm_control; 3568c2ecf20Sopenharmony_ci SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 1); 3578c2ecf20Sopenharmony_ci gbe->frm_control = val; 3588c2ecf20Sopenharmony_ci udelay(1000); 3598c2ecf20Sopenharmony_ci for (i = 0; i < 10000; i++) { 3608c2ecf20Sopenharmony_ci val = gbe->frm_inhwctrl; 3618c2ecf20Sopenharmony_ci if (GET_GBE_FIELD(FRM_INHWCTRL, FRM_DMA_ENABLE, val) != 1) 3628c2ecf20Sopenharmony_ci udelay(10); 3638c2ecf20Sopenharmony_ci else 3648c2ecf20Sopenharmony_ci break; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci if (i == 10000) 3678c2ecf20Sopenharmony_ci printk(KERN_ERR "gbefb: turn on DMA timed out\n"); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci gbe_turned_on = 1; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic void gbe_loadcmap(void) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci int i, j; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) { 3778c2ecf20Sopenharmony_ci for (j = 0; j < 1000 && gbe->cm_fifo >= 63; j++) 3788c2ecf20Sopenharmony_ci udelay(10); 3798c2ecf20Sopenharmony_ci if (j == 1000) 3808c2ecf20Sopenharmony_ci printk(KERN_ERR "gbefb: cmap FIFO timeout\n"); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci gbe->cmap[i] = gbe_cmap[i]; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* 3878c2ecf20Sopenharmony_ci * Blank the display. 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_cistatic int gbefb_blank(int blank, struct fb_info *info) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci /* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */ 3928c2ecf20Sopenharmony_ci switch (blank) { 3938c2ecf20Sopenharmony_ci case FB_BLANK_UNBLANK: /* unblank */ 3948c2ecf20Sopenharmony_ci gbe_turn_on(); 3958c2ecf20Sopenharmony_ci gbe_loadcmap(); 3968c2ecf20Sopenharmony_ci break; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci case FB_BLANK_NORMAL: /* blank */ 3998c2ecf20Sopenharmony_ci gbe_turn_off(); 4008c2ecf20Sopenharmony_ci break; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci default: 4038c2ecf20Sopenharmony_ci /* Nothing */ 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci return 0; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci/* 4108c2ecf20Sopenharmony_ci * Setup flatpanel related registers. 4118c2ecf20Sopenharmony_ci */ 4128c2ecf20Sopenharmony_cistatic void gbefb_setup_flatpanel(struct gbe_timing_info *timing) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci int fp_wid, fp_hgt, fp_vbs, fp_vbe; 4158c2ecf20Sopenharmony_ci u32 outputVal = 0; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_FLAGS, HDRV_INVERT, outputVal, 4188c2ecf20Sopenharmony_ci (timing->flags & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1); 4198c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_FLAGS, VDRV_INVERT, outputVal, 4208c2ecf20Sopenharmony_ci (timing->flags & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1); 4218c2ecf20Sopenharmony_ci gbe->vt_flags = outputVal; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* Turn on the flat panel */ 4248c2ecf20Sopenharmony_ci fp_wid = 1600; 4258c2ecf20Sopenharmony_ci fp_hgt = 1024; 4268c2ecf20Sopenharmony_ci fp_vbs = 0; 4278c2ecf20Sopenharmony_ci fp_vbe = 1600; 4288c2ecf20Sopenharmony_ci timing->pll_m = 4; 4298c2ecf20Sopenharmony_ci timing->pll_n = 1; 4308c2ecf20Sopenharmony_ci timing->pll_p = 0; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci outputVal = 0; 4338c2ecf20Sopenharmony_ci SET_GBE_FIELD(FP_DE, ON, outputVal, fp_vbs); 4348c2ecf20Sopenharmony_ci SET_GBE_FIELD(FP_DE, OFF, outputVal, fp_vbe); 4358c2ecf20Sopenharmony_ci gbe->fp_de = outputVal; 4368c2ecf20Sopenharmony_ci outputVal = 0; 4378c2ecf20Sopenharmony_ci SET_GBE_FIELD(FP_HDRV, OFF, outputVal, fp_wid); 4388c2ecf20Sopenharmony_ci gbe->fp_hdrv = outputVal; 4398c2ecf20Sopenharmony_ci outputVal = 0; 4408c2ecf20Sopenharmony_ci SET_GBE_FIELD(FP_VDRV, ON, outputVal, 1); 4418c2ecf20Sopenharmony_ci SET_GBE_FIELD(FP_VDRV, OFF, outputVal, fp_hgt + 1); 4428c2ecf20Sopenharmony_ci gbe->fp_vdrv = outputVal; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistruct gbe_pll_info { 4468c2ecf20Sopenharmony_ci int clock_rate; 4478c2ecf20Sopenharmony_ci int fvco_min; 4488c2ecf20Sopenharmony_ci int fvco_max; 4498c2ecf20Sopenharmony_ci}; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic struct gbe_pll_info gbe_pll_table[2] = { 4528c2ecf20Sopenharmony_ci { 20, 80, 220 }, 4538c2ecf20Sopenharmony_ci { 27, 80, 220 }, 4548c2ecf20Sopenharmony_ci}; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic int compute_gbe_timing(struct fb_var_screeninfo *var, 4578c2ecf20Sopenharmony_ci struct gbe_timing_info *timing) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci int pll_m, pll_n, pll_p, error, best_m, best_n, best_p, best_error; 4608c2ecf20Sopenharmony_ci int pixclock; 4618c2ecf20Sopenharmony_ci struct gbe_pll_info *gbe_pll; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (gbe_revision < 2) 4648c2ecf20Sopenharmony_ci gbe_pll = &gbe_pll_table[0]; 4658c2ecf20Sopenharmony_ci else 4668c2ecf20Sopenharmony_ci gbe_pll = &gbe_pll_table[1]; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* Determine valid resolution and timing 4698c2ecf20Sopenharmony_ci * GBE crystal runs at 20Mhz or 27Mhz 4708c2ecf20Sopenharmony_ci * pll_m, pll_n, pll_p define the following frequencies 4718c2ecf20Sopenharmony_ci * fvco = pll_m * 20Mhz / pll_n 4728c2ecf20Sopenharmony_ci * fout = fvco / (2**pll_p) */ 4738c2ecf20Sopenharmony_ci best_error = 1000000000; 4748c2ecf20Sopenharmony_ci best_n = best_m = best_p = 0; 4758c2ecf20Sopenharmony_ci for (pll_p = 0; pll_p < 4; pll_p++) 4768c2ecf20Sopenharmony_ci for (pll_m = 1; pll_m < 256; pll_m++) 4778c2ecf20Sopenharmony_ci for (pll_n = 1; pll_n < 64; pll_n++) { 4788c2ecf20Sopenharmony_ci pixclock = (1000000 / gbe_pll->clock_rate) * 4798c2ecf20Sopenharmony_ci (pll_n << pll_p) / pll_m; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci error = var->pixclock - pixclock; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (error < 0) 4848c2ecf20Sopenharmony_ci error = -error; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (error < best_error && 4878c2ecf20Sopenharmony_ci pll_m / pll_n > 4888c2ecf20Sopenharmony_ci gbe_pll->fvco_min / gbe_pll->clock_rate && 4898c2ecf20Sopenharmony_ci pll_m / pll_n < 4908c2ecf20Sopenharmony_ci gbe_pll->fvco_max / gbe_pll->clock_rate) { 4918c2ecf20Sopenharmony_ci best_error = error; 4928c2ecf20Sopenharmony_ci best_m = pll_m; 4938c2ecf20Sopenharmony_ci best_n = pll_n; 4948c2ecf20Sopenharmony_ci best_p = pll_p; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (!best_n || !best_m) 4998c2ecf20Sopenharmony_ci return -EINVAL; /* Resolution to high */ 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci pixclock = (1000000 / gbe_pll->clock_rate) * 5028c2ecf20Sopenharmony_ci (best_n << best_p) / best_m; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* set video timing information */ 5058c2ecf20Sopenharmony_ci if (timing) { 5068c2ecf20Sopenharmony_ci timing->width = var->xres; 5078c2ecf20Sopenharmony_ci timing->height = var->yres; 5088c2ecf20Sopenharmony_ci timing->pll_m = best_m; 5098c2ecf20Sopenharmony_ci timing->pll_n = best_n; 5108c2ecf20Sopenharmony_ci timing->pll_p = best_p; 5118c2ecf20Sopenharmony_ci timing->cfreq = gbe_pll->clock_rate * 1000 * timing->pll_m / 5128c2ecf20Sopenharmony_ci (timing->pll_n << timing->pll_p); 5138c2ecf20Sopenharmony_ci timing->htotal = var->left_margin + var->xres + 5148c2ecf20Sopenharmony_ci var->right_margin + var->hsync_len; 5158c2ecf20Sopenharmony_ci timing->vtotal = var->upper_margin + var->yres + 5168c2ecf20Sopenharmony_ci var->lower_margin + var->vsync_len; 5178c2ecf20Sopenharmony_ci timing->fields_sec = 1000 * timing->cfreq / timing->htotal * 5188c2ecf20Sopenharmony_ci 1000 / timing->vtotal; 5198c2ecf20Sopenharmony_ci timing->hblank_start = var->xres; 5208c2ecf20Sopenharmony_ci timing->vblank_start = var->yres; 5218c2ecf20Sopenharmony_ci timing->hblank_end = timing->htotal; 5228c2ecf20Sopenharmony_ci timing->hsync_start = var->xres + var->right_margin + 1; 5238c2ecf20Sopenharmony_ci timing->hsync_end = timing->hsync_start + var->hsync_len; 5248c2ecf20Sopenharmony_ci timing->vblank_end = timing->vtotal; 5258c2ecf20Sopenharmony_ci timing->vsync_start = var->yres + var->lower_margin + 1; 5268c2ecf20Sopenharmony_ci timing->vsync_end = timing->vsync_start + var->vsync_len; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci return pixclock; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic void gbe_set_timing_info(struct gbe_timing_info *timing) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci int temp; 5358c2ecf20Sopenharmony_ci unsigned int val; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* setup dot clock PLL */ 5388c2ecf20Sopenharmony_ci val = 0; 5398c2ecf20Sopenharmony_ci SET_GBE_FIELD(DOTCLK, M, val, timing->pll_m - 1); 5408c2ecf20Sopenharmony_ci SET_GBE_FIELD(DOTCLK, N, val, timing->pll_n - 1); 5418c2ecf20Sopenharmony_ci SET_GBE_FIELD(DOTCLK, P, val, timing->pll_p); 5428c2ecf20Sopenharmony_ci SET_GBE_FIELD(DOTCLK, RUN, val, 0); /* do not start yet */ 5438c2ecf20Sopenharmony_ci gbe->dotclock = val; 5448c2ecf20Sopenharmony_ci mdelay(10); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* setup pixel counter */ 5478c2ecf20Sopenharmony_ci val = 0; 5488c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_XYMAX, MAXX, val, timing->htotal); 5498c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_XYMAX, MAXY, val, timing->vtotal); 5508c2ecf20Sopenharmony_ci gbe->vt_xymax = val; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* setup video timing signals */ 5538c2ecf20Sopenharmony_ci val = 0; 5548c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_VSYNC, VSYNC_ON, val, timing->vsync_start); 5558c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_VSYNC, VSYNC_OFF, val, timing->vsync_end); 5568c2ecf20Sopenharmony_ci gbe->vt_vsync = val; 5578c2ecf20Sopenharmony_ci val = 0; 5588c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_HSYNC, HSYNC_ON, val, timing->hsync_start); 5598c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_HSYNC, HSYNC_OFF, val, timing->hsync_end); 5608c2ecf20Sopenharmony_ci gbe->vt_hsync = val; 5618c2ecf20Sopenharmony_ci val = 0; 5628c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_VBLANK, VBLANK_ON, val, timing->vblank_start); 5638c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_VBLANK, VBLANK_OFF, val, timing->vblank_end); 5648c2ecf20Sopenharmony_ci gbe->vt_vblank = val; 5658c2ecf20Sopenharmony_ci val = 0; 5668c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_HBLANK, HBLANK_ON, val, 5678c2ecf20Sopenharmony_ci timing->hblank_start - 5); 5688c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_HBLANK, HBLANK_OFF, val, 5698c2ecf20Sopenharmony_ci timing->hblank_end - 3); 5708c2ecf20Sopenharmony_ci gbe->vt_hblank = val; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* setup internal timing signals */ 5738c2ecf20Sopenharmony_ci val = 0; 5748c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_VCMAP, VCMAP_ON, val, timing->vblank_start); 5758c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_VCMAP, VCMAP_OFF, val, timing->vblank_end); 5768c2ecf20Sopenharmony_ci gbe->vt_vcmap = val; 5778c2ecf20Sopenharmony_ci val = 0; 5788c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_HCMAP, HCMAP_ON, val, timing->hblank_start); 5798c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_HCMAP, HCMAP_OFF, val, timing->hblank_end); 5808c2ecf20Sopenharmony_ci gbe->vt_hcmap = val; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci val = 0; 5838c2ecf20Sopenharmony_ci temp = timing->vblank_start - timing->vblank_end - 1; 5848c2ecf20Sopenharmony_ci if (temp > 0) 5858c2ecf20Sopenharmony_ci temp = -temp; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (flat_panel_enabled) 5888c2ecf20Sopenharmony_ci gbefb_setup_flatpanel(timing); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci SET_GBE_FIELD(DID_START_XY, DID_STARTY, val, (u32) temp); 5918c2ecf20Sopenharmony_ci if (timing->hblank_end >= 20) 5928c2ecf20Sopenharmony_ci SET_GBE_FIELD(DID_START_XY, DID_STARTX, val, 5938c2ecf20Sopenharmony_ci timing->hblank_end - 20); 5948c2ecf20Sopenharmony_ci else 5958c2ecf20Sopenharmony_ci SET_GBE_FIELD(DID_START_XY, DID_STARTX, val, 5968c2ecf20Sopenharmony_ci timing->htotal - (20 - timing->hblank_end)); 5978c2ecf20Sopenharmony_ci gbe->did_start_xy = val; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci val = 0; 6008c2ecf20Sopenharmony_ci SET_GBE_FIELD(CRS_START_XY, CRS_STARTY, val, (u32) (temp + 1)); 6018c2ecf20Sopenharmony_ci if (timing->hblank_end >= GBE_CRS_MAGIC) 6028c2ecf20Sopenharmony_ci SET_GBE_FIELD(CRS_START_XY, CRS_STARTX, val, 6038c2ecf20Sopenharmony_ci timing->hblank_end - GBE_CRS_MAGIC); 6048c2ecf20Sopenharmony_ci else 6058c2ecf20Sopenharmony_ci SET_GBE_FIELD(CRS_START_XY, CRS_STARTX, val, 6068c2ecf20Sopenharmony_ci timing->htotal - (GBE_CRS_MAGIC - 6078c2ecf20Sopenharmony_ci timing->hblank_end)); 6088c2ecf20Sopenharmony_ci gbe->crs_start_xy = val; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci val = 0; 6118c2ecf20Sopenharmony_ci SET_GBE_FIELD(VC_START_XY, VC_STARTY, val, (u32) temp); 6128c2ecf20Sopenharmony_ci SET_GBE_FIELD(VC_START_XY, VC_STARTX, val, timing->hblank_end - 4); 6138c2ecf20Sopenharmony_ci gbe->vc_start_xy = val; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci val = 0; 6168c2ecf20Sopenharmony_ci temp = timing->hblank_end - GBE_PIXEN_MAGIC_ON; 6178c2ecf20Sopenharmony_ci if (temp < 0) 6188c2ecf20Sopenharmony_ci temp += timing->htotal; /* allow blank to wrap around */ 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_HPIXEN, HPIXEN_ON, val, temp); 6218c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_HPIXEN, HPIXEN_OFF, val, 6228c2ecf20Sopenharmony_ci ((temp + timing->width - 6238c2ecf20Sopenharmony_ci GBE_PIXEN_MAGIC_OFF) % timing->htotal)); 6248c2ecf20Sopenharmony_ci gbe->vt_hpixen = val; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci val = 0; 6278c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_VPIXEN, VPIXEN_ON, val, timing->vblank_end); 6288c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_VPIXEN, VPIXEN_OFF, val, timing->vblank_start); 6298c2ecf20Sopenharmony_ci gbe->vt_vpixen = val; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci /* turn off sync on green */ 6328c2ecf20Sopenharmony_ci val = 0; 6338c2ecf20Sopenharmony_ci SET_GBE_FIELD(VT_FLAGS, SYNC_LOW, val, 1); 6348c2ecf20Sopenharmony_ci gbe->vt_flags = val; 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci/* 6388c2ecf20Sopenharmony_ci * Set the hardware according to 'par'. 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic int gbefb_set_par(struct fb_info *info) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci int i; 6448c2ecf20Sopenharmony_ci unsigned int val; 6458c2ecf20Sopenharmony_ci int wholeTilesX, partTilesX, maxPixelsPerTileX; 6468c2ecf20Sopenharmony_ci int height_pix; 6478c2ecf20Sopenharmony_ci int xpmax, ypmax; /* Monitor resolution */ 6488c2ecf20Sopenharmony_ci int bytesPerPixel; /* Bytes per pixel */ 6498c2ecf20Sopenharmony_ci struct gbefb_par *par = (struct gbefb_par *) info->par; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci compute_gbe_timing(&info->var, &par->timing); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci bytesPerPixel = info->var.bits_per_pixel / 8; 6548c2ecf20Sopenharmony_ci info->fix.line_length = info->var.xres_virtual * bytesPerPixel; 6558c2ecf20Sopenharmony_ci xpmax = par->timing.width; 6568c2ecf20Sopenharmony_ci ypmax = par->timing.height; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* turn off GBE */ 6598c2ecf20Sopenharmony_ci gbe_turn_off(); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci /* set timing info */ 6628c2ecf20Sopenharmony_ci gbe_set_timing_info(&par->timing); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci /* initialize DIDs */ 6658c2ecf20Sopenharmony_ci val = 0; 6668c2ecf20Sopenharmony_ci switch (bytesPerPixel) { 6678c2ecf20Sopenharmony_ci case 1: 6688c2ecf20Sopenharmony_ci SET_GBE_FIELD(WID, TYP, val, GBE_CMODE_I8); 6698c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 6708c2ecf20Sopenharmony_ci break; 6718c2ecf20Sopenharmony_ci case 2: 6728c2ecf20Sopenharmony_ci SET_GBE_FIELD(WID, TYP, val, GBE_CMODE_ARGB5); 6738c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 6748c2ecf20Sopenharmony_ci break; 6758c2ecf20Sopenharmony_ci case 4: 6768c2ecf20Sopenharmony_ci SET_GBE_FIELD(WID, TYP, val, GBE_CMODE_RGB8); 6778c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 6788c2ecf20Sopenharmony_ci break; 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci SET_GBE_FIELD(WID, BUF, val, GBE_BMODE_BOTH); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci for (i = 0; i < 32; i++) 6838c2ecf20Sopenharmony_ci gbe->mode_regs[i] = val; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* Initialize interrupts */ 6868c2ecf20Sopenharmony_ci gbe->vt_intr01 = 0xffffffff; 6878c2ecf20Sopenharmony_ci gbe->vt_intr23 = 0xffffffff; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* HACK: 6908c2ecf20Sopenharmony_ci The GBE hardware uses a tiled memory to screen mapping. Tiles are 6918c2ecf20Sopenharmony_ci blocks of 512x128, 256x128 or 128x128 pixels, respectively for 8bit, 6928c2ecf20Sopenharmony_ci 16bit and 32 bit modes (64 kB). They cover the screen with partial 6938c2ecf20Sopenharmony_ci tiles on the right and/or bottom of the screen if needed. 6948c2ecf20Sopenharmony_ci For example in 640x480 8 bit mode the mapping is: 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci <-------- 640 -----> 6978c2ecf20Sopenharmony_ci <---- 512 ----><128|384 offscreen> 6988c2ecf20Sopenharmony_ci ^ ^ 6998c2ecf20Sopenharmony_ci | 128 [tile 0] [tile 1] 7008c2ecf20Sopenharmony_ci | v 7018c2ecf20Sopenharmony_ci ^ 7028c2ecf20Sopenharmony_ci 4 128 [tile 2] [tile 3] 7038c2ecf20Sopenharmony_ci 8 v 7048c2ecf20Sopenharmony_ci 0 ^ 7058c2ecf20Sopenharmony_ci 128 [tile 4] [tile 5] 7068c2ecf20Sopenharmony_ci | v 7078c2ecf20Sopenharmony_ci | ^ 7088c2ecf20Sopenharmony_ci v 96 [tile 6] [tile 7] 7098c2ecf20Sopenharmony_ci 32 offscreen 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci Tiles have the advantage that they can be allocated individually in 7128c2ecf20Sopenharmony_ci memory. However, this mapping is not linear at all, which is not 7138c2ecf20Sopenharmony_ci really convenient. In order to support linear addressing, the GBE 7148c2ecf20Sopenharmony_ci DMA hardware is fooled into thinking the screen is only one tile 7158c2ecf20Sopenharmony_ci large and but has a greater height, so that the DMA transfer covers 7168c2ecf20Sopenharmony_ci the same region. 7178c2ecf20Sopenharmony_ci Tiles are still allocated as independent chunks of 64KB of 7188c2ecf20Sopenharmony_ci continuous physical memory and remapped so that the kernel sees the 7198c2ecf20Sopenharmony_ci framebuffer as a continuous virtual memory. The GBE tile table is 7208c2ecf20Sopenharmony_ci set up so that each tile references one of these 64k blocks: 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci GBE -> tile list framebuffer TLB <------------ CPU 7238c2ecf20Sopenharmony_ci [ tile 0 ] -> [ 64KB ] <- [ 16x 4KB page entries ] ^ 7248c2ecf20Sopenharmony_ci ... ... ... linear virtual FB 7258c2ecf20Sopenharmony_ci [ tile n ] -> [ 64KB ] <- [ 16x 4KB page entries ] v 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci The GBE hardware is then told that the buffer is 512*tweaked_height, 7298c2ecf20Sopenharmony_ci with tweaked_height = real_width*real_height/pixels_per_tile. 7308c2ecf20Sopenharmony_ci Thus the GBE hardware will scan the first tile, filing the first 64k 7318c2ecf20Sopenharmony_ci covered region of the screen, and then will proceed to the next 7328c2ecf20Sopenharmony_ci tile, until the whole screen is covered. 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci Here is what would happen at 640x480 8bit: 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci normal tiling linear 7378c2ecf20Sopenharmony_ci ^ 11111111111111112222 11111111111111111111 ^ 7388c2ecf20Sopenharmony_ci 128 11111111111111112222 11111111111111111111 102 lines 7398c2ecf20Sopenharmony_ci 11111111111111112222 11111111111111111111 v 7408c2ecf20Sopenharmony_ci V 11111111111111112222 11111111222222222222 7418c2ecf20Sopenharmony_ci 33333333333333334444 22222222222222222222 7428c2ecf20Sopenharmony_ci 33333333333333334444 22222222222222222222 7438c2ecf20Sopenharmony_ci < 512 > < 256 > 102*640+256 = 64k 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci NOTE: The only mode for which this is not working is 800x600 8bit, 7468c2ecf20Sopenharmony_ci as 800*600/512 = 937.5 which is not integer and thus causes 7478c2ecf20Sopenharmony_ci flickering. 7488c2ecf20Sopenharmony_ci I guess this is not so important as one can use 640x480 8bit or 7498c2ecf20Sopenharmony_ci 800x600 16bit anyway. 7508c2ecf20Sopenharmony_ci */ 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci /* Tell gbe about the tiles table location */ 7538c2ecf20Sopenharmony_ci /* tile_ptr -> [ tile 1 ] -> FB mem */ 7548c2ecf20Sopenharmony_ci /* [ tile 2 ] -> FB mem */ 7558c2ecf20Sopenharmony_ci /* ... */ 7568c2ecf20Sopenharmony_ci val = 0; 7578c2ecf20Sopenharmony_ci SET_GBE_FIELD(FRM_CONTROL, FRM_TILE_PTR, val, gbe_tiles.dma >> 9); 7588c2ecf20Sopenharmony_ci SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 0); /* do not start */ 7598c2ecf20Sopenharmony_ci SET_GBE_FIELD(FRM_CONTROL, FRM_LINEAR, val, 0); 7608c2ecf20Sopenharmony_ci gbe->frm_control = val; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci maxPixelsPerTileX = 512 / bytesPerPixel; 7638c2ecf20Sopenharmony_ci wholeTilesX = 1; 7648c2ecf20Sopenharmony_ci partTilesX = 0; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci /* Initialize the framebuffer */ 7678c2ecf20Sopenharmony_ci val = 0; 7688c2ecf20Sopenharmony_ci SET_GBE_FIELD(FRM_SIZE_TILE, FRM_WIDTH_TILE, val, wholeTilesX); 7698c2ecf20Sopenharmony_ci SET_GBE_FIELD(FRM_SIZE_TILE, FRM_RHS, val, partTilesX); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci switch (bytesPerPixel) { 7728c2ecf20Sopenharmony_ci case 1: 7738c2ecf20Sopenharmony_ci SET_GBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, val, 7748c2ecf20Sopenharmony_ci GBE_FRM_DEPTH_8); 7758c2ecf20Sopenharmony_ci break; 7768c2ecf20Sopenharmony_ci case 2: 7778c2ecf20Sopenharmony_ci SET_GBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, val, 7788c2ecf20Sopenharmony_ci GBE_FRM_DEPTH_16); 7798c2ecf20Sopenharmony_ci break; 7808c2ecf20Sopenharmony_ci case 4: 7818c2ecf20Sopenharmony_ci SET_GBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, val, 7828c2ecf20Sopenharmony_ci GBE_FRM_DEPTH_32); 7838c2ecf20Sopenharmony_ci break; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci gbe->frm_size_tile = val; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci /* compute tweaked height */ 7888c2ecf20Sopenharmony_ci height_pix = xpmax * ypmax / maxPixelsPerTileX; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci val = 0; 7918c2ecf20Sopenharmony_ci SET_GBE_FIELD(FRM_SIZE_PIXEL, FB_HEIGHT_PIX, val, height_pix); 7928c2ecf20Sopenharmony_ci gbe->frm_size_pixel = val; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci /* turn off DID and overlay DMA */ 7958c2ecf20Sopenharmony_ci gbe->did_control = 0; 7968c2ecf20Sopenharmony_ci gbe->ovr_width_tile = 0; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* Turn off mouse cursor */ 7998c2ecf20Sopenharmony_ci gbe->crs_ctl = 0; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci /* Turn on GBE */ 8028c2ecf20Sopenharmony_ci gbe_turn_on(); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci /* Initialize the gamma map */ 8058c2ecf20Sopenharmony_ci udelay(10); 8068c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) 8078c2ecf20Sopenharmony_ci gbe->gmap[i] = (i << 24) | (i << 16) | (i << 8); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci /* Initialize the color map */ 8108c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) 8118c2ecf20Sopenharmony_ci gbe_cmap[i] = (i << 8) | (i << 16) | (i << 24); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci gbe_loadcmap(); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci return 0; 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_cistatic void gbefb_encode_fix(struct fb_fix_screeninfo *fix, 8198c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var) 8208c2ecf20Sopenharmony_ci{ 8218c2ecf20Sopenharmony_ci memset(fix, 0, sizeof(struct fb_fix_screeninfo)); 8228c2ecf20Sopenharmony_ci strcpy(fix->id, "SGI GBE"); 8238c2ecf20Sopenharmony_ci fix->smem_start = (unsigned long) gbe_mem; 8248c2ecf20Sopenharmony_ci fix->smem_len = gbe_mem_size; 8258c2ecf20Sopenharmony_ci fix->type = FB_TYPE_PACKED_PIXELS; 8268c2ecf20Sopenharmony_ci fix->type_aux = 0; 8278c2ecf20Sopenharmony_ci fix->accel = FB_ACCEL_NONE; 8288c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 8298c2ecf20Sopenharmony_ci case 8: 8308c2ecf20Sopenharmony_ci fix->visual = FB_VISUAL_PSEUDOCOLOR; 8318c2ecf20Sopenharmony_ci break; 8328c2ecf20Sopenharmony_ci default: 8338c2ecf20Sopenharmony_ci fix->visual = FB_VISUAL_TRUECOLOR; 8348c2ecf20Sopenharmony_ci break; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci fix->ywrapstep = 0; 8378c2ecf20Sopenharmony_ci fix->xpanstep = 0; 8388c2ecf20Sopenharmony_ci fix->ypanstep = 0; 8398c2ecf20Sopenharmony_ci fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; 8408c2ecf20Sopenharmony_ci fix->mmio_start = GBE_BASE; 8418c2ecf20Sopenharmony_ci fix->mmio_len = sizeof(struct sgi_gbe); 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci/* 8458c2ecf20Sopenharmony_ci * Set a single color register. The values supplied are already 8468c2ecf20Sopenharmony_ci * rounded down to the hardware's capabilities (according to the 8478c2ecf20Sopenharmony_ci * entries in the var structure). Return != 0 for invalid regno. 8488c2ecf20Sopenharmony_ci */ 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic int gbefb_setcolreg(unsigned regno, unsigned red, unsigned green, 8518c2ecf20Sopenharmony_ci unsigned blue, unsigned transp, 8528c2ecf20Sopenharmony_ci struct fb_info *info) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci int i; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci if (regno > 255) 8578c2ecf20Sopenharmony_ci return 1; 8588c2ecf20Sopenharmony_ci red >>= 8; 8598c2ecf20Sopenharmony_ci green >>= 8; 8608c2ecf20Sopenharmony_ci blue >>= 8; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci if (info->var.bits_per_pixel <= 8) { 8638c2ecf20Sopenharmony_ci gbe_cmap[regno] = (red << 24) | (green << 16) | (blue << 8); 8648c2ecf20Sopenharmony_ci if (gbe_turned_on) { 8658c2ecf20Sopenharmony_ci /* wait for the color map FIFO to have a free entry */ 8668c2ecf20Sopenharmony_ci for (i = 0; i < 1000 && gbe->cm_fifo >= 63; i++) 8678c2ecf20Sopenharmony_ci udelay(10); 8688c2ecf20Sopenharmony_ci if (i == 1000) { 8698c2ecf20Sopenharmony_ci printk(KERN_ERR "gbefb: cmap FIFO timeout\n"); 8708c2ecf20Sopenharmony_ci return 1; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci gbe->cmap[regno] = gbe_cmap[regno]; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci } else if (regno < 16) { 8758c2ecf20Sopenharmony_ci switch (info->var.bits_per_pixel) { 8768c2ecf20Sopenharmony_ci case 15: 8778c2ecf20Sopenharmony_ci case 16: 8788c2ecf20Sopenharmony_ci red >>= 3; 8798c2ecf20Sopenharmony_ci green >>= 3; 8808c2ecf20Sopenharmony_ci blue >>= 3; 8818c2ecf20Sopenharmony_ci pseudo_palette[regno] = 8828c2ecf20Sopenharmony_ci (red << info->var.red.offset) | 8838c2ecf20Sopenharmony_ci (green << info->var.green.offset) | 8848c2ecf20Sopenharmony_ci (blue << info->var.blue.offset); 8858c2ecf20Sopenharmony_ci break; 8868c2ecf20Sopenharmony_ci case 32: 8878c2ecf20Sopenharmony_ci pseudo_palette[regno] = 8888c2ecf20Sopenharmony_ci (red << info->var.red.offset) | 8898c2ecf20Sopenharmony_ci (green << info->var.green.offset) | 8908c2ecf20Sopenharmony_ci (blue << info->var.blue.offset); 8918c2ecf20Sopenharmony_ci break; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci } 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci return 0; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci/* 8998c2ecf20Sopenharmony_ci * Check video mode validity, eventually modify var to best match. 9008c2ecf20Sopenharmony_ci */ 9018c2ecf20Sopenharmony_cistatic int gbefb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 9028c2ecf20Sopenharmony_ci{ 9038c2ecf20Sopenharmony_ci unsigned int line_length; 9048c2ecf20Sopenharmony_ci struct gbe_timing_info timing; 9058c2ecf20Sopenharmony_ci int ret; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* Limit bpp to 8, 16, and 32 */ 9088c2ecf20Sopenharmony_ci if (var->bits_per_pixel <= 8) 9098c2ecf20Sopenharmony_ci var->bits_per_pixel = 8; 9108c2ecf20Sopenharmony_ci else if (var->bits_per_pixel <= 16) 9118c2ecf20Sopenharmony_ci var->bits_per_pixel = 16; 9128c2ecf20Sopenharmony_ci else if (var->bits_per_pixel <= 32) 9138c2ecf20Sopenharmony_ci var->bits_per_pixel = 32; 9148c2ecf20Sopenharmony_ci else 9158c2ecf20Sopenharmony_ci return -EINVAL; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci /* Check the mode can be mapped linearly with the tile table trick. */ 9188c2ecf20Sopenharmony_ci /* This requires width x height x bytes/pixel be a multiple of 512 */ 9198c2ecf20Sopenharmony_ci if ((var->xres * var->yres * var->bits_per_pixel) & 4095) 9208c2ecf20Sopenharmony_ci return -EINVAL; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci var->grayscale = 0; /* No grayscale for now */ 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci ret = compute_gbe_timing(var, &timing); 9258c2ecf20Sopenharmony_ci var->pixclock = ret; 9268c2ecf20Sopenharmony_ci if (ret < 0) 9278c2ecf20Sopenharmony_ci return -EINVAL; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci /* Adjust virtual resolution, if necessary */ 9308c2ecf20Sopenharmony_ci if (var->xres > var->xres_virtual || (!ywrap && !ypan)) 9318c2ecf20Sopenharmony_ci var->xres_virtual = var->xres; 9328c2ecf20Sopenharmony_ci if (var->yres > var->yres_virtual || (!ywrap && !ypan)) 9338c2ecf20Sopenharmony_ci var->yres_virtual = var->yres; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci if (var->vmode & FB_VMODE_CONUPDATE) { 9368c2ecf20Sopenharmony_ci var->vmode |= FB_VMODE_YWRAP; 9378c2ecf20Sopenharmony_ci var->xoffset = info->var.xoffset; 9388c2ecf20Sopenharmony_ci var->yoffset = info->var.yoffset; 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci /* No grayscale for now */ 9428c2ecf20Sopenharmony_ci var->grayscale = 0; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci /* Memory limit */ 9458c2ecf20Sopenharmony_ci line_length = var->xres_virtual * var->bits_per_pixel / 8; 9468c2ecf20Sopenharmony_ci if (line_length * var->yres_virtual > gbe_mem_size) 9478c2ecf20Sopenharmony_ci return -ENOMEM; /* Virtual resolution too high */ 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 9508c2ecf20Sopenharmony_ci case 8: 9518c2ecf20Sopenharmony_ci var->red.offset = 0; 9528c2ecf20Sopenharmony_ci var->red.length = 8; 9538c2ecf20Sopenharmony_ci var->green.offset = 0; 9548c2ecf20Sopenharmony_ci var->green.length = 8; 9558c2ecf20Sopenharmony_ci var->blue.offset = 0; 9568c2ecf20Sopenharmony_ci var->blue.length = 8; 9578c2ecf20Sopenharmony_ci var->transp.offset = 0; 9588c2ecf20Sopenharmony_ci var->transp.length = 0; 9598c2ecf20Sopenharmony_ci break; 9608c2ecf20Sopenharmony_ci case 16: /* RGB 1555 */ 9618c2ecf20Sopenharmony_ci var->red.offset = 10; 9628c2ecf20Sopenharmony_ci var->red.length = 5; 9638c2ecf20Sopenharmony_ci var->green.offset = 5; 9648c2ecf20Sopenharmony_ci var->green.length = 5; 9658c2ecf20Sopenharmony_ci var->blue.offset = 0; 9668c2ecf20Sopenharmony_ci var->blue.length = 5; 9678c2ecf20Sopenharmony_ci var->transp.offset = 0; 9688c2ecf20Sopenharmony_ci var->transp.length = 0; 9698c2ecf20Sopenharmony_ci break; 9708c2ecf20Sopenharmony_ci case 32: /* RGB 8888 */ 9718c2ecf20Sopenharmony_ci var->red.offset = 24; 9728c2ecf20Sopenharmony_ci var->red.length = 8; 9738c2ecf20Sopenharmony_ci var->green.offset = 16; 9748c2ecf20Sopenharmony_ci var->green.length = 8; 9758c2ecf20Sopenharmony_ci var->blue.offset = 8; 9768c2ecf20Sopenharmony_ci var->blue.length = 8; 9778c2ecf20Sopenharmony_ci var->transp.offset = 0; 9788c2ecf20Sopenharmony_ci var->transp.length = 8; 9798c2ecf20Sopenharmony_ci break; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci var->red.msb_right = 0; 9828c2ecf20Sopenharmony_ci var->green.msb_right = 0; 9838c2ecf20Sopenharmony_ci var->blue.msb_right = 0; 9848c2ecf20Sopenharmony_ci var->transp.msb_right = 0; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci var->left_margin = timing.htotal - timing.hsync_end; 9878c2ecf20Sopenharmony_ci var->right_margin = timing.hsync_start - timing.width; 9888c2ecf20Sopenharmony_ci var->upper_margin = timing.vtotal - timing.vsync_end; 9898c2ecf20Sopenharmony_ci var->lower_margin = timing.vsync_start - timing.height; 9908c2ecf20Sopenharmony_ci var->hsync_len = timing.hsync_end - timing.hsync_start; 9918c2ecf20Sopenharmony_ci var->vsync_len = timing.vsync_end - timing.vsync_start; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci return 0; 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic int gbefb_mmap(struct fb_info *info, 9978c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci unsigned long size = vma->vm_end - vma->vm_start; 10008c2ecf20Sopenharmony_ci unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; 10018c2ecf20Sopenharmony_ci unsigned long addr; 10028c2ecf20Sopenharmony_ci unsigned long phys_addr, phys_size; 10038c2ecf20Sopenharmony_ci u16 *tile; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci /* check range */ 10068c2ecf20Sopenharmony_ci if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) 10078c2ecf20Sopenharmony_ci return -EINVAL; 10088c2ecf20Sopenharmony_ci if (size > gbe_mem_size) 10098c2ecf20Sopenharmony_ci return -EINVAL; 10108c2ecf20Sopenharmony_ci if (offset > gbe_mem_size - size) 10118c2ecf20Sopenharmony_ci return -EINVAL; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci /* remap using the fastest write-through mode on architecture */ 10148c2ecf20Sopenharmony_ci /* try not polluting the cache when possible */ 10158c2ecf20Sopenharmony_ci#ifdef CONFIG_MIPS 10168c2ecf20Sopenharmony_ci pgprot_val(vma->vm_page_prot) = 10178c2ecf20Sopenharmony_ci pgprot_fb(pgprot_val(vma->vm_page_prot)); 10188c2ecf20Sopenharmony_ci#endif 10198c2ecf20Sopenharmony_ci /* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */ 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci /* look for the starting tile */ 10228c2ecf20Sopenharmony_ci tile = &gbe_tiles.cpu[offset >> TILE_SHIFT]; 10238c2ecf20Sopenharmony_ci addr = vma->vm_start; 10248c2ecf20Sopenharmony_ci offset &= TILE_MASK; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* remap each tile separately */ 10278c2ecf20Sopenharmony_ci do { 10288c2ecf20Sopenharmony_ci phys_addr = (((unsigned long) (*tile)) << TILE_SHIFT) + offset; 10298c2ecf20Sopenharmony_ci if ((offset + size) < TILE_SIZE) 10308c2ecf20Sopenharmony_ci phys_size = size; 10318c2ecf20Sopenharmony_ci else 10328c2ecf20Sopenharmony_ci phys_size = TILE_SIZE - offset; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci if (remap_pfn_range(vma, addr, phys_addr >> PAGE_SHIFT, 10358c2ecf20Sopenharmony_ci phys_size, vma->vm_page_prot)) 10368c2ecf20Sopenharmony_ci return -EAGAIN; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci offset = 0; 10398c2ecf20Sopenharmony_ci size -= phys_size; 10408c2ecf20Sopenharmony_ci addr += phys_size; 10418c2ecf20Sopenharmony_ci tile++; 10428c2ecf20Sopenharmony_ci } while (size); 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci return 0; 10458c2ecf20Sopenharmony_ci} 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_cistatic const struct fb_ops gbefb_ops = { 10488c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 10498c2ecf20Sopenharmony_ci .fb_check_var = gbefb_check_var, 10508c2ecf20Sopenharmony_ci .fb_set_par = gbefb_set_par, 10518c2ecf20Sopenharmony_ci .fb_setcolreg = gbefb_setcolreg, 10528c2ecf20Sopenharmony_ci .fb_mmap = gbefb_mmap, 10538c2ecf20Sopenharmony_ci .fb_blank = gbefb_blank, 10548c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 10558c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 10568c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 10578c2ecf20Sopenharmony_ci}; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci/* 10608c2ecf20Sopenharmony_ci * sysfs 10618c2ecf20Sopenharmony_ci */ 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_cistatic ssize_t gbefb_show_memsize(struct device *dev, struct device_attribute *attr, char *buf) 10648c2ecf20Sopenharmony_ci{ 10658c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%u\n", gbe_mem_size); 10668c2ecf20Sopenharmony_ci} 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_cistatic DEVICE_ATTR(size, S_IRUGO, gbefb_show_memsize, NULL); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_cistatic ssize_t gbefb_show_rev(struct device *device, struct device_attribute *attr, char *buf) 10718c2ecf20Sopenharmony_ci{ 10728c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", gbe_revision); 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cistatic DEVICE_ATTR(revision, S_IRUGO, gbefb_show_rev, NULL); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_cistatic void gbefb_remove_sysfs(struct device *dev) 10788c2ecf20Sopenharmony_ci{ 10798c2ecf20Sopenharmony_ci device_remove_file(dev, &dev_attr_size); 10808c2ecf20Sopenharmony_ci device_remove_file(dev, &dev_attr_revision); 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_cistatic void gbefb_create_sysfs(struct device *dev) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci device_create_file(dev, &dev_attr_size); 10868c2ecf20Sopenharmony_ci device_create_file(dev, &dev_attr_revision); 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci/* 10908c2ecf20Sopenharmony_ci * Initialization 10918c2ecf20Sopenharmony_ci */ 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_cistatic int gbefb_setup(char *options) 10948c2ecf20Sopenharmony_ci{ 10958c2ecf20Sopenharmony_ci char *this_opt; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci if (!options || !*options) 10988c2ecf20Sopenharmony_ci return 0; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci while ((this_opt = strsep(&options, ",")) != NULL) { 11018c2ecf20Sopenharmony_ci if (!strncmp(this_opt, "monitor:", 8)) { 11028c2ecf20Sopenharmony_ci if (!strncmp(this_opt + 8, "crt", 3)) { 11038c2ecf20Sopenharmony_ci flat_panel_enabled = 0; 11048c2ecf20Sopenharmony_ci default_var = &default_var_CRT; 11058c2ecf20Sopenharmony_ci default_mode = &default_mode_CRT; 11068c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt + 8, "1600sw", 6) || 11078c2ecf20Sopenharmony_ci !strncmp(this_opt + 8, "lcd", 3)) { 11088c2ecf20Sopenharmony_ci flat_panel_enabled = 1; 11098c2ecf20Sopenharmony_ci default_var = &default_var_LCD; 11108c2ecf20Sopenharmony_ci default_mode = &default_mode_LCD; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "mem:", 4)) { 11138c2ecf20Sopenharmony_ci gbe_mem_size = memparse(this_opt + 4, &this_opt); 11148c2ecf20Sopenharmony_ci if (gbe_mem_size > CONFIG_FB_GBE_MEM * 1024 * 1024) 11158c2ecf20Sopenharmony_ci gbe_mem_size = CONFIG_FB_GBE_MEM * 1024 * 1024; 11168c2ecf20Sopenharmony_ci if (gbe_mem_size < TILE_SIZE) 11178c2ecf20Sopenharmony_ci gbe_mem_size = TILE_SIZE; 11188c2ecf20Sopenharmony_ci } else 11198c2ecf20Sopenharmony_ci mode_option = this_opt; 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci return 0; 11228c2ecf20Sopenharmony_ci} 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_cistatic int gbefb_probe(struct platform_device *p_dev) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci int i, ret = 0; 11278c2ecf20Sopenharmony_ci struct fb_info *info; 11288c2ecf20Sopenharmony_ci struct gbefb_par *par; 11298c2ecf20Sopenharmony_ci#ifndef MODULE 11308c2ecf20Sopenharmony_ci char *options = NULL; 11318c2ecf20Sopenharmony_ci#endif 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct gbefb_par), &p_dev->dev); 11348c2ecf20Sopenharmony_ci if (!info) 11358c2ecf20Sopenharmony_ci return -ENOMEM; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci#ifndef MODULE 11388c2ecf20Sopenharmony_ci if (fb_get_options("gbefb", &options)) { 11398c2ecf20Sopenharmony_ci ret = -ENODEV; 11408c2ecf20Sopenharmony_ci goto out_release_framebuffer; 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci gbefb_setup(options); 11438c2ecf20Sopenharmony_ci#endif 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci if (!request_mem_region(GBE_BASE, sizeof(struct sgi_gbe), "GBE")) { 11468c2ecf20Sopenharmony_ci printk(KERN_ERR "gbefb: couldn't reserve mmio region\n"); 11478c2ecf20Sopenharmony_ci ret = -EBUSY; 11488c2ecf20Sopenharmony_ci goto out_release_framebuffer; 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci gbe = (struct sgi_gbe *) devm_ioremap(&p_dev->dev, GBE_BASE, 11528c2ecf20Sopenharmony_ci sizeof(struct sgi_gbe)); 11538c2ecf20Sopenharmony_ci if (!gbe) { 11548c2ecf20Sopenharmony_ci printk(KERN_ERR "gbefb: couldn't map mmio region\n"); 11558c2ecf20Sopenharmony_ci ret = -ENXIO; 11568c2ecf20Sopenharmony_ci goto out_release_mem_region; 11578c2ecf20Sopenharmony_ci } 11588c2ecf20Sopenharmony_ci gbe_revision = gbe->ctrlstat & 15; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci gbe_tiles.cpu = dmam_alloc_coherent(&p_dev->dev, 11618c2ecf20Sopenharmony_ci GBE_TLB_SIZE * sizeof(uint16_t), 11628c2ecf20Sopenharmony_ci &gbe_tiles.dma, GFP_KERNEL); 11638c2ecf20Sopenharmony_ci if (!gbe_tiles.cpu) { 11648c2ecf20Sopenharmony_ci printk(KERN_ERR "gbefb: couldn't allocate tiles table\n"); 11658c2ecf20Sopenharmony_ci ret = -ENOMEM; 11668c2ecf20Sopenharmony_ci goto out_release_mem_region; 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci if (gbe_mem_phys) { 11708c2ecf20Sopenharmony_ci /* memory was allocated at boot time */ 11718c2ecf20Sopenharmony_ci gbe_mem = devm_ioremap_wc(&p_dev->dev, gbe_mem_phys, 11728c2ecf20Sopenharmony_ci gbe_mem_size); 11738c2ecf20Sopenharmony_ci if (!gbe_mem) { 11748c2ecf20Sopenharmony_ci printk(KERN_ERR "gbefb: couldn't map framebuffer\n"); 11758c2ecf20Sopenharmony_ci ret = -ENOMEM; 11768c2ecf20Sopenharmony_ci goto out_release_mem_region; 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci gbe_dma_addr = 0; 11808c2ecf20Sopenharmony_ci } else { 11818c2ecf20Sopenharmony_ci /* try to allocate memory with the classical allocator 11828c2ecf20Sopenharmony_ci * this has high chance to fail on low memory machines */ 11838c2ecf20Sopenharmony_ci gbe_mem = dmam_alloc_attrs(&p_dev->dev, gbe_mem_size, 11848c2ecf20Sopenharmony_ci &gbe_dma_addr, GFP_KERNEL, 11858c2ecf20Sopenharmony_ci DMA_ATTR_WRITE_COMBINE); 11868c2ecf20Sopenharmony_ci if (!gbe_mem) { 11878c2ecf20Sopenharmony_ci printk(KERN_ERR "gbefb: couldn't allocate framebuffer memory\n"); 11888c2ecf20Sopenharmony_ci ret = -ENOMEM; 11898c2ecf20Sopenharmony_ci goto out_release_mem_region; 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci gbe_mem_phys = (unsigned long) gbe_dma_addr; 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci par = info->par; 11968c2ecf20Sopenharmony_ci par->wc_cookie = arch_phys_wc_add(gbe_mem_phys, gbe_mem_size); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci /* map framebuffer memory into tiles table */ 11998c2ecf20Sopenharmony_ci for (i = 0; i < (gbe_mem_size >> TILE_SHIFT); i++) 12008c2ecf20Sopenharmony_ci gbe_tiles.cpu[i] = (gbe_mem_phys >> TILE_SHIFT) + i; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci info->fbops = &gbefb_ops; 12038c2ecf20Sopenharmony_ci info->pseudo_palette = pseudo_palette; 12048c2ecf20Sopenharmony_ci info->flags = FBINFO_DEFAULT; 12058c2ecf20Sopenharmony_ci info->screen_base = gbe_mem; 12068c2ecf20Sopenharmony_ci fb_alloc_cmap(&info->cmap, 256, 0); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci /* reset GBE */ 12098c2ecf20Sopenharmony_ci gbe_reset(); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci /* turn on default video mode */ 12128c2ecf20Sopenharmony_ci if (fb_find_mode(&par->var, info, mode_option, NULL, 0, 12138c2ecf20Sopenharmony_ci default_mode, 8) == 0) 12148c2ecf20Sopenharmony_ci par->var = *default_var; 12158c2ecf20Sopenharmony_ci info->var = par->var; 12168c2ecf20Sopenharmony_ci gbefb_check_var(&par->var, info); 12178c2ecf20Sopenharmony_ci gbefb_encode_fix(&info->fix, &info->var); 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci if (register_framebuffer(info) < 0) { 12208c2ecf20Sopenharmony_ci printk(KERN_ERR "gbefb: couldn't register framebuffer\n"); 12218c2ecf20Sopenharmony_ci ret = -ENXIO; 12228c2ecf20Sopenharmony_ci goto out_gbe_unmap; 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci platform_set_drvdata(p_dev, info); 12268c2ecf20Sopenharmony_ci gbefb_create_sysfs(&p_dev->dev); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci fb_info(info, "%s rev %d @ 0x%08x using %dkB memory\n", 12298c2ecf20Sopenharmony_ci info->fix.id, gbe_revision, (unsigned)GBE_BASE, 12308c2ecf20Sopenharmony_ci gbe_mem_size >> 10); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci return 0; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ciout_gbe_unmap: 12358c2ecf20Sopenharmony_ci arch_phys_wc_del(par->wc_cookie); 12368c2ecf20Sopenharmony_ciout_release_mem_region: 12378c2ecf20Sopenharmony_ci release_mem_region(GBE_BASE, sizeof(struct sgi_gbe)); 12388c2ecf20Sopenharmony_ciout_release_framebuffer: 12398c2ecf20Sopenharmony_ci framebuffer_release(info); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci return ret; 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_cistatic int gbefb_remove(struct platform_device* p_dev) 12458c2ecf20Sopenharmony_ci{ 12468c2ecf20Sopenharmony_ci struct fb_info *info = platform_get_drvdata(p_dev); 12478c2ecf20Sopenharmony_ci struct gbefb_par *par = info->par; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci unregister_framebuffer(info); 12508c2ecf20Sopenharmony_ci gbe_turn_off(); 12518c2ecf20Sopenharmony_ci arch_phys_wc_del(par->wc_cookie); 12528c2ecf20Sopenharmony_ci release_mem_region(GBE_BASE, sizeof(struct sgi_gbe)); 12538c2ecf20Sopenharmony_ci gbefb_remove_sysfs(&p_dev->dev); 12548c2ecf20Sopenharmony_ci framebuffer_release(info); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci return 0; 12578c2ecf20Sopenharmony_ci} 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_cistatic struct platform_driver gbefb_driver = { 12608c2ecf20Sopenharmony_ci .probe = gbefb_probe, 12618c2ecf20Sopenharmony_ci .remove = gbefb_remove, 12628c2ecf20Sopenharmony_ci .driver = { 12638c2ecf20Sopenharmony_ci .name = "gbefb", 12648c2ecf20Sopenharmony_ci }, 12658c2ecf20Sopenharmony_ci}; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_cistatic struct platform_device *gbefb_device; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_cistatic int __init gbefb_init(void) 12708c2ecf20Sopenharmony_ci{ 12718c2ecf20Sopenharmony_ci int ret = platform_driver_register(&gbefb_driver); 12728c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_SGI_IP32) && !ret) { 12738c2ecf20Sopenharmony_ci gbefb_device = platform_device_alloc("gbefb", 0); 12748c2ecf20Sopenharmony_ci if (gbefb_device) { 12758c2ecf20Sopenharmony_ci ret = platform_device_add(gbefb_device); 12768c2ecf20Sopenharmony_ci } else { 12778c2ecf20Sopenharmony_ci ret = -ENOMEM; 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci if (ret) { 12808c2ecf20Sopenharmony_ci platform_device_put(gbefb_device); 12818c2ecf20Sopenharmony_ci platform_driver_unregister(&gbefb_driver); 12828c2ecf20Sopenharmony_ci } 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci return ret; 12858c2ecf20Sopenharmony_ci} 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_cistatic void __exit gbefb_exit(void) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci platform_device_unregister(gbefb_device); 12908c2ecf20Sopenharmony_ci platform_driver_unregister(&gbefb_driver); 12918c2ecf20Sopenharmony_ci} 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_cimodule_init(gbefb_init); 12948c2ecf20Sopenharmony_cimodule_exit(gbefb_exit); 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1297