162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * SGI GBE frame buffer driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 1999 Silicon Graphics, Inc. - Jeffrey Newquist 562306a36Sopenharmony_ci * Copyright (C) 2002 Vivien Chappelier <vivien.chappelier@linux-mips.org> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 862306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 962306a36Sopenharmony_ci * more details. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1562306a36Sopenharmony_ci#include <linux/errno.h> 1662306a36Sopenharmony_ci#include <linux/gfp.h> 1762306a36Sopenharmony_ci#include <linux/fb.h> 1862306a36Sopenharmony_ci#include <linux/init.h> 1962306a36Sopenharmony_ci#include <linux/interrupt.h> 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/mm.h> 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/io.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#ifdef CONFIG_MIPS 2662306a36Sopenharmony_ci#include <asm/addrspace.h> 2762306a36Sopenharmony_ci#endif 2862306a36Sopenharmony_ci#include <asm/byteorder.h> 2962306a36Sopenharmony_ci#include <asm/tlbflush.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <video/gbe.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic struct sgi_gbe *gbe; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct gbefb_par { 3662306a36Sopenharmony_ci struct fb_var_screeninfo var; 3762306a36Sopenharmony_ci struct gbe_timing_info timing; 3862306a36Sopenharmony_ci int wc_cookie; 3962306a36Sopenharmony_ci int valid; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define GBE_BASE 0x16000000 /* SGI O2 */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* macro for fastest write-though access to the framebuffer */ 4562306a36Sopenharmony_ci#ifdef CONFIG_MIPS 4662306a36Sopenharmony_ci#ifdef CONFIG_CPU_R10000 4762306a36Sopenharmony_ci#define pgprot_fb(_prot) (((_prot) & (~_CACHE_MASK)) | _CACHE_UNCACHED_ACCELERATED) 4862306a36Sopenharmony_ci#else 4962306a36Sopenharmony_ci#define pgprot_fb(_prot) (((_prot) & (~_CACHE_MASK)) | _CACHE_CACHABLE_NO_WA) 5062306a36Sopenharmony_ci#endif 5162306a36Sopenharmony_ci#endif 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* 5462306a36Sopenharmony_ci * RAM we reserve for the frame buffer. This defines the maximum screen 5562306a36Sopenharmony_ci * size 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci#if CONFIG_FB_GBE_MEM > 8 5862306a36Sopenharmony_ci#error GBE Framebuffer cannot use more than 8MB of memory 5962306a36Sopenharmony_ci#endif 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define TILE_SHIFT 16 6262306a36Sopenharmony_ci#define TILE_SIZE (1 << TILE_SHIFT) 6362306a36Sopenharmony_ci#define TILE_MASK (TILE_SIZE - 1) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic unsigned int gbe_mem_size = CONFIG_FB_GBE_MEM * 1024*1024; 6662306a36Sopenharmony_cistatic void *gbe_mem; 6762306a36Sopenharmony_cistatic dma_addr_t gbe_dma_addr; 6862306a36Sopenharmony_cistatic unsigned long gbe_mem_phys; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic struct { 7162306a36Sopenharmony_ci uint16_t *cpu; 7262306a36Sopenharmony_ci dma_addr_t dma; 7362306a36Sopenharmony_ci} gbe_tiles; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int gbe_revision; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int ypan, ywrap; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic uint32_t pseudo_palette[16]; 8062306a36Sopenharmony_cistatic uint32_t gbe_cmap[256]; 8162306a36Sopenharmony_cistatic int gbe_turned_on; /* 0 turned off, 1 turned on */ 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic char *mode_option = NULL; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* default CRT mode */ 8662306a36Sopenharmony_cistatic struct fb_var_screeninfo default_var_CRT = { 8762306a36Sopenharmony_ci /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */ 8862306a36Sopenharmony_ci .xres = 640, 8962306a36Sopenharmony_ci .yres = 480, 9062306a36Sopenharmony_ci .xres_virtual = 640, 9162306a36Sopenharmony_ci .yres_virtual = 480, 9262306a36Sopenharmony_ci .xoffset = 0, 9362306a36Sopenharmony_ci .yoffset = 0, 9462306a36Sopenharmony_ci .bits_per_pixel = 8, 9562306a36Sopenharmony_ci .grayscale = 0, 9662306a36Sopenharmony_ci .red = { 0, 8, 0 }, 9762306a36Sopenharmony_ci .green = { 0, 8, 0 }, 9862306a36Sopenharmony_ci .blue = { 0, 8, 0 }, 9962306a36Sopenharmony_ci .transp = { 0, 0, 0 }, 10062306a36Sopenharmony_ci .nonstd = 0, 10162306a36Sopenharmony_ci .activate = 0, 10262306a36Sopenharmony_ci .height = -1, 10362306a36Sopenharmony_ci .width = -1, 10462306a36Sopenharmony_ci .accel_flags = 0, 10562306a36Sopenharmony_ci .pixclock = 39722, /* picoseconds */ 10662306a36Sopenharmony_ci .left_margin = 48, 10762306a36Sopenharmony_ci .right_margin = 16, 10862306a36Sopenharmony_ci .upper_margin = 33, 10962306a36Sopenharmony_ci .lower_margin = 10, 11062306a36Sopenharmony_ci .hsync_len = 96, 11162306a36Sopenharmony_ci .vsync_len = 2, 11262306a36Sopenharmony_ci .sync = 0, 11362306a36Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* default LCD mode */ 11762306a36Sopenharmony_cistatic struct fb_var_screeninfo default_var_LCD = { 11862306a36Sopenharmony_ci /* 1600x1024, 8 bpp */ 11962306a36Sopenharmony_ci .xres = 1600, 12062306a36Sopenharmony_ci .yres = 1024, 12162306a36Sopenharmony_ci .xres_virtual = 1600, 12262306a36Sopenharmony_ci .yres_virtual = 1024, 12362306a36Sopenharmony_ci .xoffset = 0, 12462306a36Sopenharmony_ci .yoffset = 0, 12562306a36Sopenharmony_ci .bits_per_pixel = 8, 12662306a36Sopenharmony_ci .grayscale = 0, 12762306a36Sopenharmony_ci .red = { 0, 8, 0 }, 12862306a36Sopenharmony_ci .green = { 0, 8, 0 }, 12962306a36Sopenharmony_ci .blue = { 0, 8, 0 }, 13062306a36Sopenharmony_ci .transp = { 0, 0, 0 }, 13162306a36Sopenharmony_ci .nonstd = 0, 13262306a36Sopenharmony_ci .activate = 0, 13362306a36Sopenharmony_ci .height = -1, 13462306a36Sopenharmony_ci .width = -1, 13562306a36Sopenharmony_ci .accel_flags = 0, 13662306a36Sopenharmony_ci .pixclock = 9353, 13762306a36Sopenharmony_ci .left_margin = 20, 13862306a36Sopenharmony_ci .right_margin = 30, 13962306a36Sopenharmony_ci .upper_margin = 37, 14062306a36Sopenharmony_ci .lower_margin = 3, 14162306a36Sopenharmony_ci .hsync_len = 20, 14262306a36Sopenharmony_ci .vsync_len = 3, 14362306a36Sopenharmony_ci .sync = 0, 14462306a36Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* default modedb mode */ 14862306a36Sopenharmony_ci/* 640x480, 60 Hz, Non-Interlaced (25.172 MHz dotclock) */ 14962306a36Sopenharmony_cistatic struct fb_videomode default_mode_CRT = { 15062306a36Sopenharmony_ci .refresh = 60, 15162306a36Sopenharmony_ci .xres = 640, 15262306a36Sopenharmony_ci .yres = 480, 15362306a36Sopenharmony_ci .pixclock = 39722, 15462306a36Sopenharmony_ci .left_margin = 48, 15562306a36Sopenharmony_ci .right_margin = 16, 15662306a36Sopenharmony_ci .upper_margin = 33, 15762306a36Sopenharmony_ci .lower_margin = 10, 15862306a36Sopenharmony_ci .hsync_len = 96, 15962306a36Sopenharmony_ci .vsync_len = 2, 16062306a36Sopenharmony_ci .sync = 0, 16162306a36Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci/* 1600x1024 SGI flatpanel 1600sw */ 16462306a36Sopenharmony_cistatic struct fb_videomode default_mode_LCD = { 16562306a36Sopenharmony_ci /* 1600x1024, 8 bpp */ 16662306a36Sopenharmony_ci .xres = 1600, 16762306a36Sopenharmony_ci .yres = 1024, 16862306a36Sopenharmony_ci .pixclock = 9353, 16962306a36Sopenharmony_ci .left_margin = 20, 17062306a36Sopenharmony_ci .right_margin = 30, 17162306a36Sopenharmony_ci .upper_margin = 37, 17262306a36Sopenharmony_ci .lower_margin = 3, 17362306a36Sopenharmony_ci .hsync_len = 20, 17462306a36Sopenharmony_ci .vsync_len = 3, 17562306a36Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 17662306a36Sopenharmony_ci}; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic struct fb_videomode *default_mode = &default_mode_CRT; 17962306a36Sopenharmony_cistatic struct fb_var_screeninfo *default_var = &default_var_CRT; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int flat_panel_enabled = 0; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic void gbe_reset(void) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci /* Turn on dotclock PLL */ 18662306a36Sopenharmony_ci gbe->ctrlstat = 0x300aa000; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* 19162306a36Sopenharmony_ci * Function: gbe_turn_off 19262306a36Sopenharmony_ci * Parameters: (None) 19362306a36Sopenharmony_ci * Description: This should turn off the monitor and gbe. This is used 19462306a36Sopenharmony_ci * when switching between the serial console and the graphics 19562306a36Sopenharmony_ci * console. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic void gbe_turn_off(void) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci int i; 20162306a36Sopenharmony_ci unsigned int val, y, vpixen_off; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci gbe_turned_on = 0; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* check if pixel counter is on */ 20662306a36Sopenharmony_ci val = gbe->vt_xy; 20762306a36Sopenharmony_ci if (GET_GBE_FIELD(VT_XY, FREEZE, val) == 1) 20862306a36Sopenharmony_ci return; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* turn off DMA */ 21162306a36Sopenharmony_ci val = gbe->ovr_control; 21262306a36Sopenharmony_ci SET_GBE_FIELD(OVR_CONTROL, OVR_DMA_ENABLE, val, 0); 21362306a36Sopenharmony_ci gbe->ovr_control = val; 21462306a36Sopenharmony_ci udelay(1000); 21562306a36Sopenharmony_ci val = gbe->frm_control; 21662306a36Sopenharmony_ci SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 0); 21762306a36Sopenharmony_ci gbe->frm_control = val; 21862306a36Sopenharmony_ci udelay(1000); 21962306a36Sopenharmony_ci val = gbe->did_control; 22062306a36Sopenharmony_ci SET_GBE_FIELD(DID_CONTROL, DID_DMA_ENABLE, val, 0); 22162306a36Sopenharmony_ci gbe->did_control = val; 22262306a36Sopenharmony_ci udelay(1000); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* We have to wait through two vertical retrace periods before 22562306a36Sopenharmony_ci * the pixel DMA is turned off for sure. */ 22662306a36Sopenharmony_ci for (i = 0; i < 10000; i++) { 22762306a36Sopenharmony_ci val = gbe->frm_inhwctrl; 22862306a36Sopenharmony_ci if (GET_GBE_FIELD(FRM_INHWCTRL, FRM_DMA_ENABLE, val)) { 22962306a36Sopenharmony_ci udelay(10); 23062306a36Sopenharmony_ci } else { 23162306a36Sopenharmony_ci val = gbe->ovr_inhwctrl; 23262306a36Sopenharmony_ci if (GET_GBE_FIELD(OVR_INHWCTRL, OVR_DMA_ENABLE, val)) { 23362306a36Sopenharmony_ci udelay(10); 23462306a36Sopenharmony_ci } else { 23562306a36Sopenharmony_ci val = gbe->did_inhwctrl; 23662306a36Sopenharmony_ci if (GET_GBE_FIELD(DID_INHWCTRL, DID_DMA_ENABLE, val)) { 23762306a36Sopenharmony_ci udelay(10); 23862306a36Sopenharmony_ci } else 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci if (i == 10000) 24462306a36Sopenharmony_ci printk(KERN_ERR "gbefb: turn off DMA timed out\n"); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* wait for vpixen_off */ 24762306a36Sopenharmony_ci val = gbe->vt_vpixen; 24862306a36Sopenharmony_ci vpixen_off = GET_GBE_FIELD(VT_VPIXEN, VPIXEN_OFF, val); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci for (i = 0; i < 100000; i++) { 25162306a36Sopenharmony_ci val = gbe->vt_xy; 25262306a36Sopenharmony_ci y = GET_GBE_FIELD(VT_XY, Y, val); 25362306a36Sopenharmony_ci if (y < vpixen_off) 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci udelay(1); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci if (i == 100000) 25862306a36Sopenharmony_ci printk(KERN_ERR 25962306a36Sopenharmony_ci "gbefb: wait for vpixen_off timed out\n"); 26062306a36Sopenharmony_ci for (i = 0; i < 10000; i++) { 26162306a36Sopenharmony_ci val = gbe->vt_xy; 26262306a36Sopenharmony_ci y = GET_GBE_FIELD(VT_XY, Y, val); 26362306a36Sopenharmony_ci if (y > vpixen_off) 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci udelay(1); 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci if (i == 10000) 26862306a36Sopenharmony_ci printk(KERN_ERR "gbefb: wait for vpixen_off timed out\n"); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* turn off pixel counter */ 27162306a36Sopenharmony_ci val = 0; 27262306a36Sopenharmony_ci SET_GBE_FIELD(VT_XY, FREEZE, val, 1); 27362306a36Sopenharmony_ci gbe->vt_xy = val; 27462306a36Sopenharmony_ci mdelay(10); 27562306a36Sopenharmony_ci for (i = 0; i < 10000; i++) { 27662306a36Sopenharmony_ci val = gbe->vt_xy; 27762306a36Sopenharmony_ci if (GET_GBE_FIELD(VT_XY, FREEZE, val) != 1) 27862306a36Sopenharmony_ci udelay(10); 27962306a36Sopenharmony_ci else 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci if (i == 10000) 28362306a36Sopenharmony_ci printk(KERN_ERR "gbefb: turn off pixel clock timed out\n"); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* turn off dot clock */ 28662306a36Sopenharmony_ci val = gbe->dotclock; 28762306a36Sopenharmony_ci SET_GBE_FIELD(DOTCLK, RUN, val, 0); 28862306a36Sopenharmony_ci gbe->dotclock = val; 28962306a36Sopenharmony_ci mdelay(10); 29062306a36Sopenharmony_ci for (i = 0; i < 10000; i++) { 29162306a36Sopenharmony_ci val = gbe->dotclock; 29262306a36Sopenharmony_ci if (GET_GBE_FIELD(DOTCLK, RUN, val)) 29362306a36Sopenharmony_ci udelay(10); 29462306a36Sopenharmony_ci else 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci if (i == 10000) 29862306a36Sopenharmony_ci printk(KERN_ERR "gbefb: turn off dotclock timed out\n"); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* reset the frame DMA FIFO */ 30162306a36Sopenharmony_ci val = gbe->frm_size_tile; 30262306a36Sopenharmony_ci SET_GBE_FIELD(FRM_SIZE_TILE, FRM_FIFO_RESET, val, 1); 30362306a36Sopenharmony_ci gbe->frm_size_tile = val; 30462306a36Sopenharmony_ci SET_GBE_FIELD(FRM_SIZE_TILE, FRM_FIFO_RESET, val, 0); 30562306a36Sopenharmony_ci gbe->frm_size_tile = val; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic void gbe_turn_on(void) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci unsigned int val, i; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* 31362306a36Sopenharmony_ci * Check if pixel counter is off, for unknown reason this 31462306a36Sopenharmony_ci * code hangs Visual Workstations 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_ci if (gbe_revision < 2) { 31762306a36Sopenharmony_ci val = gbe->vt_xy; 31862306a36Sopenharmony_ci if (GET_GBE_FIELD(VT_XY, FREEZE, val) == 0) 31962306a36Sopenharmony_ci return; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* turn on dot clock */ 32362306a36Sopenharmony_ci val = gbe->dotclock; 32462306a36Sopenharmony_ci SET_GBE_FIELD(DOTCLK, RUN, val, 1); 32562306a36Sopenharmony_ci gbe->dotclock = val; 32662306a36Sopenharmony_ci mdelay(10); 32762306a36Sopenharmony_ci for (i = 0; i < 10000; i++) { 32862306a36Sopenharmony_ci val = gbe->dotclock; 32962306a36Sopenharmony_ci if (GET_GBE_FIELD(DOTCLK, RUN, val) != 1) 33062306a36Sopenharmony_ci udelay(10); 33162306a36Sopenharmony_ci else 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci if (i == 10000) 33562306a36Sopenharmony_ci printk(KERN_ERR "gbefb: turn on dotclock timed out\n"); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* turn on pixel counter */ 33862306a36Sopenharmony_ci val = 0; 33962306a36Sopenharmony_ci SET_GBE_FIELD(VT_XY, FREEZE, val, 0); 34062306a36Sopenharmony_ci gbe->vt_xy = val; 34162306a36Sopenharmony_ci mdelay(10); 34262306a36Sopenharmony_ci for (i = 0; i < 10000; i++) { 34362306a36Sopenharmony_ci val = gbe->vt_xy; 34462306a36Sopenharmony_ci if (GET_GBE_FIELD(VT_XY, FREEZE, val)) 34562306a36Sopenharmony_ci udelay(10); 34662306a36Sopenharmony_ci else 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci if (i == 10000) 35062306a36Sopenharmony_ci printk(KERN_ERR "gbefb: turn on pixel clock timed out\n"); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* turn on DMA */ 35362306a36Sopenharmony_ci val = gbe->frm_control; 35462306a36Sopenharmony_ci SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 1); 35562306a36Sopenharmony_ci gbe->frm_control = val; 35662306a36Sopenharmony_ci udelay(1000); 35762306a36Sopenharmony_ci for (i = 0; i < 10000; i++) { 35862306a36Sopenharmony_ci val = gbe->frm_inhwctrl; 35962306a36Sopenharmony_ci if (GET_GBE_FIELD(FRM_INHWCTRL, FRM_DMA_ENABLE, val) != 1) 36062306a36Sopenharmony_ci udelay(10); 36162306a36Sopenharmony_ci else 36262306a36Sopenharmony_ci break; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci if (i == 10000) 36562306a36Sopenharmony_ci printk(KERN_ERR "gbefb: turn on DMA timed out\n"); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci gbe_turned_on = 1; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic void gbe_loadcmap(void) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci int i, j; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 37562306a36Sopenharmony_ci for (j = 0; j < 1000 && gbe->cm_fifo >= 63; j++) 37662306a36Sopenharmony_ci udelay(10); 37762306a36Sopenharmony_ci if (j == 1000) 37862306a36Sopenharmony_ci printk(KERN_ERR "gbefb: cmap FIFO timeout\n"); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci gbe->cmap[i] = gbe_cmap[i]; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/* 38562306a36Sopenharmony_ci * Blank the display. 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_cistatic int gbefb_blank(int blank, struct fb_info *info) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci /* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */ 39062306a36Sopenharmony_ci switch (blank) { 39162306a36Sopenharmony_ci case FB_BLANK_UNBLANK: /* unblank */ 39262306a36Sopenharmony_ci gbe_turn_on(); 39362306a36Sopenharmony_ci gbe_loadcmap(); 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci case FB_BLANK_NORMAL: /* blank */ 39762306a36Sopenharmony_ci gbe_turn_off(); 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci default: 40162306a36Sopenharmony_ci /* Nothing */ 40262306a36Sopenharmony_ci break; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci return 0; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci/* 40862306a36Sopenharmony_ci * Setup flatpanel related registers. 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_cistatic void gbefb_setup_flatpanel(struct gbe_timing_info *timing) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci int fp_wid, fp_hgt, fp_vbs, fp_vbe; 41362306a36Sopenharmony_ci u32 outputVal = 0; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci SET_GBE_FIELD(VT_FLAGS, HDRV_INVERT, outputVal, 41662306a36Sopenharmony_ci (timing->flags & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1); 41762306a36Sopenharmony_ci SET_GBE_FIELD(VT_FLAGS, VDRV_INVERT, outputVal, 41862306a36Sopenharmony_ci (timing->flags & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1); 41962306a36Sopenharmony_ci gbe->vt_flags = outputVal; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* Turn on the flat panel */ 42262306a36Sopenharmony_ci fp_wid = 1600; 42362306a36Sopenharmony_ci fp_hgt = 1024; 42462306a36Sopenharmony_ci fp_vbs = 0; 42562306a36Sopenharmony_ci fp_vbe = 1600; 42662306a36Sopenharmony_ci timing->pll_m = 4; 42762306a36Sopenharmony_ci timing->pll_n = 1; 42862306a36Sopenharmony_ci timing->pll_p = 0; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci outputVal = 0; 43162306a36Sopenharmony_ci SET_GBE_FIELD(FP_DE, ON, outputVal, fp_vbs); 43262306a36Sopenharmony_ci SET_GBE_FIELD(FP_DE, OFF, outputVal, fp_vbe); 43362306a36Sopenharmony_ci gbe->fp_de = outputVal; 43462306a36Sopenharmony_ci outputVal = 0; 43562306a36Sopenharmony_ci SET_GBE_FIELD(FP_HDRV, OFF, outputVal, fp_wid); 43662306a36Sopenharmony_ci gbe->fp_hdrv = outputVal; 43762306a36Sopenharmony_ci outputVal = 0; 43862306a36Sopenharmony_ci SET_GBE_FIELD(FP_VDRV, ON, outputVal, 1); 43962306a36Sopenharmony_ci SET_GBE_FIELD(FP_VDRV, OFF, outputVal, fp_hgt + 1); 44062306a36Sopenharmony_ci gbe->fp_vdrv = outputVal; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistruct gbe_pll_info { 44462306a36Sopenharmony_ci int clock_rate; 44562306a36Sopenharmony_ci int fvco_min; 44662306a36Sopenharmony_ci int fvco_max; 44762306a36Sopenharmony_ci}; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic struct gbe_pll_info gbe_pll_table[2] = { 45062306a36Sopenharmony_ci { 20, 80, 220 }, 45162306a36Sopenharmony_ci { 27, 80, 220 }, 45262306a36Sopenharmony_ci}; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int compute_gbe_timing(struct fb_var_screeninfo *var, 45562306a36Sopenharmony_ci struct gbe_timing_info *timing) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci int pll_m, pll_n, pll_p, error, best_m, best_n, best_p, best_error; 45862306a36Sopenharmony_ci int pixclock; 45962306a36Sopenharmony_ci struct gbe_pll_info *gbe_pll; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (gbe_revision < 2) 46262306a36Sopenharmony_ci gbe_pll = &gbe_pll_table[0]; 46362306a36Sopenharmony_ci else 46462306a36Sopenharmony_ci gbe_pll = &gbe_pll_table[1]; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* Determine valid resolution and timing 46762306a36Sopenharmony_ci * GBE crystal runs at 20Mhz or 27Mhz 46862306a36Sopenharmony_ci * pll_m, pll_n, pll_p define the following frequencies 46962306a36Sopenharmony_ci * fvco = pll_m * 20Mhz / pll_n 47062306a36Sopenharmony_ci * fout = fvco / (2**pll_p) */ 47162306a36Sopenharmony_ci best_error = 1000000000; 47262306a36Sopenharmony_ci best_n = best_m = best_p = 0; 47362306a36Sopenharmony_ci for (pll_p = 0; pll_p < 4; pll_p++) 47462306a36Sopenharmony_ci for (pll_m = 1; pll_m < 256; pll_m++) 47562306a36Sopenharmony_ci for (pll_n = 1; pll_n < 64; pll_n++) { 47662306a36Sopenharmony_ci pixclock = (1000000 / gbe_pll->clock_rate) * 47762306a36Sopenharmony_ci (pll_n << pll_p) / pll_m; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci error = var->pixclock - pixclock; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (error < 0) 48262306a36Sopenharmony_ci error = -error; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (error < best_error && 48562306a36Sopenharmony_ci pll_m / pll_n > 48662306a36Sopenharmony_ci gbe_pll->fvco_min / gbe_pll->clock_rate && 48762306a36Sopenharmony_ci pll_m / pll_n < 48862306a36Sopenharmony_ci gbe_pll->fvco_max / gbe_pll->clock_rate) { 48962306a36Sopenharmony_ci best_error = error; 49062306a36Sopenharmony_ci best_m = pll_m; 49162306a36Sopenharmony_ci best_n = pll_n; 49262306a36Sopenharmony_ci best_p = pll_p; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (!best_n || !best_m) 49762306a36Sopenharmony_ci return -EINVAL; /* Resolution to high */ 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci pixclock = (1000000 / gbe_pll->clock_rate) * 50062306a36Sopenharmony_ci (best_n << best_p) / best_m; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* set video timing information */ 50362306a36Sopenharmony_ci if (timing) { 50462306a36Sopenharmony_ci timing->width = var->xres; 50562306a36Sopenharmony_ci timing->height = var->yres; 50662306a36Sopenharmony_ci timing->pll_m = best_m; 50762306a36Sopenharmony_ci timing->pll_n = best_n; 50862306a36Sopenharmony_ci timing->pll_p = best_p; 50962306a36Sopenharmony_ci timing->cfreq = gbe_pll->clock_rate * 1000 * timing->pll_m / 51062306a36Sopenharmony_ci (timing->pll_n << timing->pll_p); 51162306a36Sopenharmony_ci timing->htotal = var->left_margin + var->xres + 51262306a36Sopenharmony_ci var->right_margin + var->hsync_len; 51362306a36Sopenharmony_ci timing->vtotal = var->upper_margin + var->yres + 51462306a36Sopenharmony_ci var->lower_margin + var->vsync_len; 51562306a36Sopenharmony_ci timing->fields_sec = 1000 * timing->cfreq / timing->htotal * 51662306a36Sopenharmony_ci 1000 / timing->vtotal; 51762306a36Sopenharmony_ci timing->hblank_start = var->xres; 51862306a36Sopenharmony_ci timing->vblank_start = var->yres; 51962306a36Sopenharmony_ci timing->hblank_end = timing->htotal; 52062306a36Sopenharmony_ci timing->hsync_start = var->xres + var->right_margin + 1; 52162306a36Sopenharmony_ci timing->hsync_end = timing->hsync_start + var->hsync_len; 52262306a36Sopenharmony_ci timing->vblank_end = timing->vtotal; 52362306a36Sopenharmony_ci timing->vsync_start = var->yres + var->lower_margin + 1; 52462306a36Sopenharmony_ci timing->vsync_end = timing->vsync_start + var->vsync_len; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return pixclock; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic void gbe_set_timing_info(struct gbe_timing_info *timing) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci int temp; 53362306a36Sopenharmony_ci unsigned int val; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* setup dot clock PLL */ 53662306a36Sopenharmony_ci val = 0; 53762306a36Sopenharmony_ci SET_GBE_FIELD(DOTCLK, M, val, timing->pll_m - 1); 53862306a36Sopenharmony_ci SET_GBE_FIELD(DOTCLK, N, val, timing->pll_n - 1); 53962306a36Sopenharmony_ci SET_GBE_FIELD(DOTCLK, P, val, timing->pll_p); 54062306a36Sopenharmony_ci SET_GBE_FIELD(DOTCLK, RUN, val, 0); /* do not start yet */ 54162306a36Sopenharmony_ci gbe->dotclock = val; 54262306a36Sopenharmony_ci mdelay(10); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* setup pixel counter */ 54562306a36Sopenharmony_ci val = 0; 54662306a36Sopenharmony_ci SET_GBE_FIELD(VT_XYMAX, MAXX, val, timing->htotal); 54762306a36Sopenharmony_ci SET_GBE_FIELD(VT_XYMAX, MAXY, val, timing->vtotal); 54862306a36Sopenharmony_ci gbe->vt_xymax = val; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* setup video timing signals */ 55162306a36Sopenharmony_ci val = 0; 55262306a36Sopenharmony_ci SET_GBE_FIELD(VT_VSYNC, VSYNC_ON, val, timing->vsync_start); 55362306a36Sopenharmony_ci SET_GBE_FIELD(VT_VSYNC, VSYNC_OFF, val, timing->vsync_end); 55462306a36Sopenharmony_ci gbe->vt_vsync = val; 55562306a36Sopenharmony_ci val = 0; 55662306a36Sopenharmony_ci SET_GBE_FIELD(VT_HSYNC, HSYNC_ON, val, timing->hsync_start); 55762306a36Sopenharmony_ci SET_GBE_FIELD(VT_HSYNC, HSYNC_OFF, val, timing->hsync_end); 55862306a36Sopenharmony_ci gbe->vt_hsync = val; 55962306a36Sopenharmony_ci val = 0; 56062306a36Sopenharmony_ci SET_GBE_FIELD(VT_VBLANK, VBLANK_ON, val, timing->vblank_start); 56162306a36Sopenharmony_ci SET_GBE_FIELD(VT_VBLANK, VBLANK_OFF, val, timing->vblank_end); 56262306a36Sopenharmony_ci gbe->vt_vblank = val; 56362306a36Sopenharmony_ci val = 0; 56462306a36Sopenharmony_ci SET_GBE_FIELD(VT_HBLANK, HBLANK_ON, val, 56562306a36Sopenharmony_ci timing->hblank_start - 5); 56662306a36Sopenharmony_ci SET_GBE_FIELD(VT_HBLANK, HBLANK_OFF, val, 56762306a36Sopenharmony_ci timing->hblank_end - 3); 56862306a36Sopenharmony_ci gbe->vt_hblank = val; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* setup internal timing signals */ 57162306a36Sopenharmony_ci val = 0; 57262306a36Sopenharmony_ci SET_GBE_FIELD(VT_VCMAP, VCMAP_ON, val, timing->vblank_start); 57362306a36Sopenharmony_ci SET_GBE_FIELD(VT_VCMAP, VCMAP_OFF, val, timing->vblank_end); 57462306a36Sopenharmony_ci gbe->vt_vcmap = val; 57562306a36Sopenharmony_ci val = 0; 57662306a36Sopenharmony_ci SET_GBE_FIELD(VT_HCMAP, HCMAP_ON, val, timing->hblank_start); 57762306a36Sopenharmony_ci SET_GBE_FIELD(VT_HCMAP, HCMAP_OFF, val, timing->hblank_end); 57862306a36Sopenharmony_ci gbe->vt_hcmap = val; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci val = 0; 58162306a36Sopenharmony_ci temp = timing->vblank_start - timing->vblank_end - 1; 58262306a36Sopenharmony_ci if (temp > 0) 58362306a36Sopenharmony_ci temp = -temp; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (flat_panel_enabled) 58662306a36Sopenharmony_ci gbefb_setup_flatpanel(timing); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci SET_GBE_FIELD(DID_START_XY, DID_STARTY, val, (u32) temp); 58962306a36Sopenharmony_ci if (timing->hblank_end >= 20) 59062306a36Sopenharmony_ci SET_GBE_FIELD(DID_START_XY, DID_STARTX, val, 59162306a36Sopenharmony_ci timing->hblank_end - 20); 59262306a36Sopenharmony_ci else 59362306a36Sopenharmony_ci SET_GBE_FIELD(DID_START_XY, DID_STARTX, val, 59462306a36Sopenharmony_ci timing->htotal - (20 - timing->hblank_end)); 59562306a36Sopenharmony_ci gbe->did_start_xy = val; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci val = 0; 59862306a36Sopenharmony_ci SET_GBE_FIELD(CRS_START_XY, CRS_STARTY, val, (u32) (temp + 1)); 59962306a36Sopenharmony_ci if (timing->hblank_end >= GBE_CRS_MAGIC) 60062306a36Sopenharmony_ci SET_GBE_FIELD(CRS_START_XY, CRS_STARTX, val, 60162306a36Sopenharmony_ci timing->hblank_end - GBE_CRS_MAGIC); 60262306a36Sopenharmony_ci else 60362306a36Sopenharmony_ci SET_GBE_FIELD(CRS_START_XY, CRS_STARTX, val, 60462306a36Sopenharmony_ci timing->htotal - (GBE_CRS_MAGIC - 60562306a36Sopenharmony_ci timing->hblank_end)); 60662306a36Sopenharmony_ci gbe->crs_start_xy = val; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci val = 0; 60962306a36Sopenharmony_ci SET_GBE_FIELD(VC_START_XY, VC_STARTY, val, (u32) temp); 61062306a36Sopenharmony_ci SET_GBE_FIELD(VC_START_XY, VC_STARTX, val, timing->hblank_end - 4); 61162306a36Sopenharmony_ci gbe->vc_start_xy = val; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci val = 0; 61462306a36Sopenharmony_ci temp = timing->hblank_end - GBE_PIXEN_MAGIC_ON; 61562306a36Sopenharmony_ci if (temp < 0) 61662306a36Sopenharmony_ci temp += timing->htotal; /* allow blank to wrap around */ 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci SET_GBE_FIELD(VT_HPIXEN, HPIXEN_ON, val, temp); 61962306a36Sopenharmony_ci SET_GBE_FIELD(VT_HPIXEN, HPIXEN_OFF, val, 62062306a36Sopenharmony_ci ((temp + timing->width - 62162306a36Sopenharmony_ci GBE_PIXEN_MAGIC_OFF) % timing->htotal)); 62262306a36Sopenharmony_ci gbe->vt_hpixen = val; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci val = 0; 62562306a36Sopenharmony_ci SET_GBE_FIELD(VT_VPIXEN, VPIXEN_ON, val, timing->vblank_end); 62662306a36Sopenharmony_ci SET_GBE_FIELD(VT_VPIXEN, VPIXEN_OFF, val, timing->vblank_start); 62762306a36Sopenharmony_ci gbe->vt_vpixen = val; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* turn off sync on green */ 63062306a36Sopenharmony_ci val = 0; 63162306a36Sopenharmony_ci SET_GBE_FIELD(VT_FLAGS, SYNC_LOW, val, 1); 63262306a36Sopenharmony_ci gbe->vt_flags = val; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci/* 63662306a36Sopenharmony_ci * Set the hardware according to 'par'. 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cistatic int gbefb_set_par(struct fb_info *info) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci int i; 64262306a36Sopenharmony_ci unsigned int val; 64362306a36Sopenharmony_ci int wholeTilesX, partTilesX, maxPixelsPerTileX; 64462306a36Sopenharmony_ci int height_pix; 64562306a36Sopenharmony_ci int xpmax, ypmax; /* Monitor resolution */ 64662306a36Sopenharmony_ci int bytesPerPixel; /* Bytes per pixel */ 64762306a36Sopenharmony_ci struct gbefb_par *par = (struct gbefb_par *) info->par; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci compute_gbe_timing(&info->var, &par->timing); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci bytesPerPixel = info->var.bits_per_pixel / 8; 65262306a36Sopenharmony_ci info->fix.line_length = info->var.xres_virtual * bytesPerPixel; 65362306a36Sopenharmony_ci xpmax = par->timing.width; 65462306a36Sopenharmony_ci ypmax = par->timing.height; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* turn off GBE */ 65762306a36Sopenharmony_ci gbe_turn_off(); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* set timing info */ 66062306a36Sopenharmony_ci gbe_set_timing_info(&par->timing); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* initialize DIDs */ 66362306a36Sopenharmony_ci val = 0; 66462306a36Sopenharmony_ci switch (bytesPerPixel) { 66562306a36Sopenharmony_ci case 1: 66662306a36Sopenharmony_ci SET_GBE_FIELD(WID, TYP, val, GBE_CMODE_I8); 66762306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 66862306a36Sopenharmony_ci break; 66962306a36Sopenharmony_ci case 2: 67062306a36Sopenharmony_ci SET_GBE_FIELD(WID, TYP, val, GBE_CMODE_ARGB5); 67162306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci case 4: 67462306a36Sopenharmony_ci SET_GBE_FIELD(WID, TYP, val, GBE_CMODE_RGB8); 67562306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 67662306a36Sopenharmony_ci break; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci SET_GBE_FIELD(WID, BUF, val, GBE_BMODE_BOTH); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci for (i = 0; i < 32; i++) 68162306a36Sopenharmony_ci gbe->mode_regs[i] = val; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci /* Initialize interrupts */ 68462306a36Sopenharmony_ci gbe->vt_intr01 = 0xffffffff; 68562306a36Sopenharmony_ci gbe->vt_intr23 = 0xffffffff; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* HACK: 68862306a36Sopenharmony_ci The GBE hardware uses a tiled memory to screen mapping. Tiles are 68962306a36Sopenharmony_ci blocks of 512x128, 256x128 or 128x128 pixels, respectively for 8bit, 69062306a36Sopenharmony_ci 16bit and 32 bit modes (64 kB). They cover the screen with partial 69162306a36Sopenharmony_ci tiles on the right and/or bottom of the screen if needed. 69262306a36Sopenharmony_ci For example in 640x480 8 bit mode the mapping is: 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci <-------- 640 -----> 69562306a36Sopenharmony_ci <---- 512 ----><128|384 offscreen> 69662306a36Sopenharmony_ci ^ ^ 69762306a36Sopenharmony_ci | 128 [tile 0] [tile 1] 69862306a36Sopenharmony_ci | v 69962306a36Sopenharmony_ci ^ 70062306a36Sopenharmony_ci 4 128 [tile 2] [tile 3] 70162306a36Sopenharmony_ci 8 v 70262306a36Sopenharmony_ci 0 ^ 70362306a36Sopenharmony_ci 128 [tile 4] [tile 5] 70462306a36Sopenharmony_ci | v 70562306a36Sopenharmony_ci | ^ 70662306a36Sopenharmony_ci v 96 [tile 6] [tile 7] 70762306a36Sopenharmony_ci 32 offscreen 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci Tiles have the advantage that they can be allocated individually in 71062306a36Sopenharmony_ci memory. However, this mapping is not linear at all, which is not 71162306a36Sopenharmony_ci really convenient. In order to support linear addressing, the GBE 71262306a36Sopenharmony_ci DMA hardware is fooled into thinking the screen is only one tile 71362306a36Sopenharmony_ci large and but has a greater height, so that the DMA transfer covers 71462306a36Sopenharmony_ci the same region. 71562306a36Sopenharmony_ci Tiles are still allocated as independent chunks of 64KB of 71662306a36Sopenharmony_ci continuous physical memory and remapped so that the kernel sees the 71762306a36Sopenharmony_ci framebuffer as a continuous virtual memory. The GBE tile table is 71862306a36Sopenharmony_ci set up so that each tile references one of these 64k blocks: 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci GBE -> tile list framebuffer TLB <------------ CPU 72162306a36Sopenharmony_ci [ tile 0 ] -> [ 64KB ] <- [ 16x 4KB page entries ] ^ 72262306a36Sopenharmony_ci ... ... ... linear virtual FB 72362306a36Sopenharmony_ci [ tile n ] -> [ 64KB ] <- [ 16x 4KB page entries ] v 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci The GBE hardware is then told that the buffer is 512*tweaked_height, 72762306a36Sopenharmony_ci with tweaked_height = real_width*real_height/pixels_per_tile. 72862306a36Sopenharmony_ci Thus the GBE hardware will scan the first tile, filing the first 64k 72962306a36Sopenharmony_ci covered region of the screen, and then will proceed to the next 73062306a36Sopenharmony_ci tile, until the whole screen is covered. 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci Here is what would happen at 640x480 8bit: 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci normal tiling linear 73562306a36Sopenharmony_ci ^ 11111111111111112222 11111111111111111111 ^ 73662306a36Sopenharmony_ci 128 11111111111111112222 11111111111111111111 102 lines 73762306a36Sopenharmony_ci 11111111111111112222 11111111111111111111 v 73862306a36Sopenharmony_ci V 11111111111111112222 11111111222222222222 73962306a36Sopenharmony_ci 33333333333333334444 22222222222222222222 74062306a36Sopenharmony_ci 33333333333333334444 22222222222222222222 74162306a36Sopenharmony_ci < 512 > < 256 > 102*640+256 = 64k 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci NOTE: The only mode for which this is not working is 800x600 8bit, 74462306a36Sopenharmony_ci as 800*600/512 = 937.5 which is not integer and thus causes 74562306a36Sopenharmony_ci flickering. 74662306a36Sopenharmony_ci I guess this is not so important as one can use 640x480 8bit or 74762306a36Sopenharmony_ci 800x600 16bit anyway. 74862306a36Sopenharmony_ci */ 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* Tell gbe about the tiles table location */ 75162306a36Sopenharmony_ci /* tile_ptr -> [ tile 1 ] -> FB mem */ 75262306a36Sopenharmony_ci /* [ tile 2 ] -> FB mem */ 75362306a36Sopenharmony_ci /* ... */ 75462306a36Sopenharmony_ci val = 0; 75562306a36Sopenharmony_ci SET_GBE_FIELD(FRM_CONTROL, FRM_TILE_PTR, val, gbe_tiles.dma >> 9); 75662306a36Sopenharmony_ci SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 0); /* do not start */ 75762306a36Sopenharmony_ci SET_GBE_FIELD(FRM_CONTROL, FRM_LINEAR, val, 0); 75862306a36Sopenharmony_ci gbe->frm_control = val; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci maxPixelsPerTileX = 512 / bytesPerPixel; 76162306a36Sopenharmony_ci wholeTilesX = 1; 76262306a36Sopenharmony_ci partTilesX = 0; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* Initialize the framebuffer */ 76562306a36Sopenharmony_ci val = 0; 76662306a36Sopenharmony_ci SET_GBE_FIELD(FRM_SIZE_TILE, FRM_WIDTH_TILE, val, wholeTilesX); 76762306a36Sopenharmony_ci SET_GBE_FIELD(FRM_SIZE_TILE, FRM_RHS, val, partTilesX); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci switch (bytesPerPixel) { 77062306a36Sopenharmony_ci case 1: 77162306a36Sopenharmony_ci SET_GBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, val, 77262306a36Sopenharmony_ci GBE_FRM_DEPTH_8); 77362306a36Sopenharmony_ci break; 77462306a36Sopenharmony_ci case 2: 77562306a36Sopenharmony_ci SET_GBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, val, 77662306a36Sopenharmony_ci GBE_FRM_DEPTH_16); 77762306a36Sopenharmony_ci break; 77862306a36Sopenharmony_ci case 4: 77962306a36Sopenharmony_ci SET_GBE_FIELD(FRM_SIZE_TILE, FRM_DEPTH, val, 78062306a36Sopenharmony_ci GBE_FRM_DEPTH_32); 78162306a36Sopenharmony_ci break; 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci gbe->frm_size_tile = val; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci /* compute tweaked height */ 78662306a36Sopenharmony_ci height_pix = xpmax * ypmax / maxPixelsPerTileX; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci val = 0; 78962306a36Sopenharmony_ci SET_GBE_FIELD(FRM_SIZE_PIXEL, FB_HEIGHT_PIX, val, height_pix); 79062306a36Sopenharmony_ci gbe->frm_size_pixel = val; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci /* turn off DID and overlay DMA */ 79362306a36Sopenharmony_ci gbe->did_control = 0; 79462306a36Sopenharmony_ci gbe->ovr_width_tile = 0; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* Turn off mouse cursor */ 79762306a36Sopenharmony_ci gbe->crs_ctl = 0; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci /* Turn on GBE */ 80062306a36Sopenharmony_ci gbe_turn_on(); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* Initialize the gamma map */ 80362306a36Sopenharmony_ci udelay(10); 80462306a36Sopenharmony_ci for (i = 0; i < 256; i++) 80562306a36Sopenharmony_ci gbe->gmap[i] = (i << 24) | (i << 16) | (i << 8); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* Initialize the color map */ 80862306a36Sopenharmony_ci for (i = 0; i < 256; i++) 80962306a36Sopenharmony_ci gbe_cmap[i] = (i << 8) | (i << 16) | (i << 24); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci gbe_loadcmap(); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci return 0; 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic void gbefb_encode_fix(struct fb_fix_screeninfo *fix, 81762306a36Sopenharmony_ci struct fb_var_screeninfo *var) 81862306a36Sopenharmony_ci{ 81962306a36Sopenharmony_ci memset(fix, 0, sizeof(struct fb_fix_screeninfo)); 82062306a36Sopenharmony_ci strcpy(fix->id, "SGI GBE"); 82162306a36Sopenharmony_ci fix->smem_start = (unsigned long) gbe_mem; 82262306a36Sopenharmony_ci fix->smem_len = gbe_mem_size; 82362306a36Sopenharmony_ci fix->type = FB_TYPE_PACKED_PIXELS; 82462306a36Sopenharmony_ci fix->type_aux = 0; 82562306a36Sopenharmony_ci fix->accel = FB_ACCEL_NONE; 82662306a36Sopenharmony_ci switch (var->bits_per_pixel) { 82762306a36Sopenharmony_ci case 8: 82862306a36Sopenharmony_ci fix->visual = FB_VISUAL_PSEUDOCOLOR; 82962306a36Sopenharmony_ci break; 83062306a36Sopenharmony_ci default: 83162306a36Sopenharmony_ci fix->visual = FB_VISUAL_TRUECOLOR; 83262306a36Sopenharmony_ci break; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci fix->ywrapstep = 0; 83562306a36Sopenharmony_ci fix->xpanstep = 0; 83662306a36Sopenharmony_ci fix->ypanstep = 0; 83762306a36Sopenharmony_ci fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; 83862306a36Sopenharmony_ci fix->mmio_start = GBE_BASE; 83962306a36Sopenharmony_ci fix->mmio_len = sizeof(struct sgi_gbe); 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci/* 84362306a36Sopenharmony_ci * Set a single color register. The values supplied are already 84462306a36Sopenharmony_ci * rounded down to the hardware's capabilities (according to the 84562306a36Sopenharmony_ci * entries in the var structure). Return != 0 for invalid regno. 84662306a36Sopenharmony_ci */ 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cistatic int gbefb_setcolreg(unsigned regno, unsigned red, unsigned green, 84962306a36Sopenharmony_ci unsigned blue, unsigned transp, 85062306a36Sopenharmony_ci struct fb_info *info) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci int i; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (regno > 255) 85562306a36Sopenharmony_ci return 1; 85662306a36Sopenharmony_ci red >>= 8; 85762306a36Sopenharmony_ci green >>= 8; 85862306a36Sopenharmony_ci blue >>= 8; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci if (info->var.bits_per_pixel <= 8) { 86162306a36Sopenharmony_ci gbe_cmap[regno] = (red << 24) | (green << 16) | (blue << 8); 86262306a36Sopenharmony_ci if (gbe_turned_on) { 86362306a36Sopenharmony_ci /* wait for the color map FIFO to have a free entry */ 86462306a36Sopenharmony_ci for (i = 0; i < 1000 && gbe->cm_fifo >= 63; i++) 86562306a36Sopenharmony_ci udelay(10); 86662306a36Sopenharmony_ci if (i == 1000) { 86762306a36Sopenharmony_ci printk(KERN_ERR "gbefb: cmap FIFO timeout\n"); 86862306a36Sopenharmony_ci return 1; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci gbe->cmap[regno] = gbe_cmap[regno]; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci } else if (regno < 16) { 87362306a36Sopenharmony_ci switch (info->var.bits_per_pixel) { 87462306a36Sopenharmony_ci case 15: 87562306a36Sopenharmony_ci case 16: 87662306a36Sopenharmony_ci red >>= 3; 87762306a36Sopenharmony_ci green >>= 3; 87862306a36Sopenharmony_ci blue >>= 3; 87962306a36Sopenharmony_ci pseudo_palette[regno] = 88062306a36Sopenharmony_ci (red << info->var.red.offset) | 88162306a36Sopenharmony_ci (green << info->var.green.offset) | 88262306a36Sopenharmony_ci (blue << info->var.blue.offset); 88362306a36Sopenharmony_ci break; 88462306a36Sopenharmony_ci case 32: 88562306a36Sopenharmony_ci pseudo_palette[regno] = 88662306a36Sopenharmony_ci (red << info->var.red.offset) | 88762306a36Sopenharmony_ci (green << info->var.green.offset) | 88862306a36Sopenharmony_ci (blue << info->var.blue.offset); 88962306a36Sopenharmony_ci break; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci return 0; 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci/* 89762306a36Sopenharmony_ci * Check video mode validity, eventually modify var to best match. 89862306a36Sopenharmony_ci */ 89962306a36Sopenharmony_cistatic int gbefb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci unsigned int line_length; 90262306a36Sopenharmony_ci struct gbe_timing_info timing; 90362306a36Sopenharmony_ci int ret; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci /* Limit bpp to 8, 16, and 32 */ 90662306a36Sopenharmony_ci if (var->bits_per_pixel <= 8) 90762306a36Sopenharmony_ci var->bits_per_pixel = 8; 90862306a36Sopenharmony_ci else if (var->bits_per_pixel <= 16) 90962306a36Sopenharmony_ci var->bits_per_pixel = 16; 91062306a36Sopenharmony_ci else if (var->bits_per_pixel <= 32) 91162306a36Sopenharmony_ci var->bits_per_pixel = 32; 91262306a36Sopenharmony_ci else 91362306a36Sopenharmony_ci return -EINVAL; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci /* Check the mode can be mapped linearly with the tile table trick. */ 91662306a36Sopenharmony_ci /* This requires width x height x bytes/pixel be a multiple of 512 */ 91762306a36Sopenharmony_ci if ((var->xres * var->yres * var->bits_per_pixel) & 4095) 91862306a36Sopenharmony_ci return -EINVAL; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci var->grayscale = 0; /* No grayscale for now */ 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci ret = compute_gbe_timing(var, &timing); 92362306a36Sopenharmony_ci var->pixclock = ret; 92462306a36Sopenharmony_ci if (ret < 0) 92562306a36Sopenharmony_ci return -EINVAL; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci /* Adjust virtual resolution, if necessary */ 92862306a36Sopenharmony_ci if (var->xres > var->xres_virtual || (!ywrap && !ypan)) 92962306a36Sopenharmony_ci var->xres_virtual = var->xres; 93062306a36Sopenharmony_ci if (var->yres > var->yres_virtual || (!ywrap && !ypan)) 93162306a36Sopenharmony_ci var->yres_virtual = var->yres; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci if (var->vmode & FB_VMODE_CONUPDATE) { 93462306a36Sopenharmony_ci var->vmode |= FB_VMODE_YWRAP; 93562306a36Sopenharmony_ci var->xoffset = info->var.xoffset; 93662306a36Sopenharmony_ci var->yoffset = info->var.yoffset; 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci /* No grayscale for now */ 94062306a36Sopenharmony_ci var->grayscale = 0; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci /* Memory limit */ 94362306a36Sopenharmony_ci line_length = var->xres_virtual * var->bits_per_pixel / 8; 94462306a36Sopenharmony_ci if (line_length * var->yres_virtual > gbe_mem_size) 94562306a36Sopenharmony_ci return -ENOMEM; /* Virtual resolution too high */ 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci switch (var->bits_per_pixel) { 94862306a36Sopenharmony_ci case 8: 94962306a36Sopenharmony_ci var->red.offset = 0; 95062306a36Sopenharmony_ci var->red.length = 8; 95162306a36Sopenharmony_ci var->green.offset = 0; 95262306a36Sopenharmony_ci var->green.length = 8; 95362306a36Sopenharmony_ci var->blue.offset = 0; 95462306a36Sopenharmony_ci var->blue.length = 8; 95562306a36Sopenharmony_ci var->transp.offset = 0; 95662306a36Sopenharmony_ci var->transp.length = 0; 95762306a36Sopenharmony_ci break; 95862306a36Sopenharmony_ci case 16: /* RGB 1555 */ 95962306a36Sopenharmony_ci var->red.offset = 10; 96062306a36Sopenharmony_ci var->red.length = 5; 96162306a36Sopenharmony_ci var->green.offset = 5; 96262306a36Sopenharmony_ci var->green.length = 5; 96362306a36Sopenharmony_ci var->blue.offset = 0; 96462306a36Sopenharmony_ci var->blue.length = 5; 96562306a36Sopenharmony_ci var->transp.offset = 0; 96662306a36Sopenharmony_ci var->transp.length = 0; 96762306a36Sopenharmony_ci break; 96862306a36Sopenharmony_ci case 32: /* RGB 8888 */ 96962306a36Sopenharmony_ci var->red.offset = 24; 97062306a36Sopenharmony_ci var->red.length = 8; 97162306a36Sopenharmony_ci var->green.offset = 16; 97262306a36Sopenharmony_ci var->green.length = 8; 97362306a36Sopenharmony_ci var->blue.offset = 8; 97462306a36Sopenharmony_ci var->blue.length = 8; 97562306a36Sopenharmony_ci var->transp.offset = 0; 97662306a36Sopenharmony_ci var->transp.length = 8; 97762306a36Sopenharmony_ci break; 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci var->red.msb_right = 0; 98062306a36Sopenharmony_ci var->green.msb_right = 0; 98162306a36Sopenharmony_ci var->blue.msb_right = 0; 98262306a36Sopenharmony_ci var->transp.msb_right = 0; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci var->left_margin = timing.htotal - timing.hsync_end; 98562306a36Sopenharmony_ci var->right_margin = timing.hsync_start - timing.width; 98662306a36Sopenharmony_ci var->upper_margin = timing.vtotal - timing.vsync_end; 98762306a36Sopenharmony_ci var->lower_margin = timing.vsync_start - timing.height; 98862306a36Sopenharmony_ci var->hsync_len = timing.hsync_end - timing.hsync_start; 98962306a36Sopenharmony_ci var->vsync_len = timing.vsync_end - timing.vsync_start; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci return 0; 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic int gbefb_mmap(struct fb_info *info, 99562306a36Sopenharmony_ci struct vm_area_struct *vma) 99662306a36Sopenharmony_ci{ 99762306a36Sopenharmony_ci unsigned long size = vma->vm_end - vma->vm_start; 99862306a36Sopenharmony_ci unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; 99962306a36Sopenharmony_ci unsigned long addr; 100062306a36Sopenharmony_ci unsigned long phys_addr, phys_size; 100162306a36Sopenharmony_ci u16 *tile; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci /* check range */ 100462306a36Sopenharmony_ci if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) 100562306a36Sopenharmony_ci return -EINVAL; 100662306a36Sopenharmony_ci if (size > gbe_mem_size) 100762306a36Sopenharmony_ci return -EINVAL; 100862306a36Sopenharmony_ci if (offset > gbe_mem_size - size) 100962306a36Sopenharmony_ci return -EINVAL; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci /* remap using the fastest write-through mode on architecture */ 101262306a36Sopenharmony_ci /* try not polluting the cache when possible */ 101362306a36Sopenharmony_ci#ifdef CONFIG_MIPS 101462306a36Sopenharmony_ci pgprot_val(vma->vm_page_prot) = 101562306a36Sopenharmony_ci pgprot_fb(pgprot_val(vma->vm_page_prot)); 101662306a36Sopenharmony_ci#endif 101762306a36Sopenharmony_ci /* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */ 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci /* look for the starting tile */ 102062306a36Sopenharmony_ci tile = &gbe_tiles.cpu[offset >> TILE_SHIFT]; 102162306a36Sopenharmony_ci addr = vma->vm_start; 102262306a36Sopenharmony_ci offset &= TILE_MASK; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci /* remap each tile separately */ 102562306a36Sopenharmony_ci do { 102662306a36Sopenharmony_ci phys_addr = (((unsigned long) (*tile)) << TILE_SHIFT) + offset; 102762306a36Sopenharmony_ci if ((offset + size) < TILE_SIZE) 102862306a36Sopenharmony_ci phys_size = size; 102962306a36Sopenharmony_ci else 103062306a36Sopenharmony_ci phys_size = TILE_SIZE - offset; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci if (remap_pfn_range(vma, addr, phys_addr >> PAGE_SHIFT, 103362306a36Sopenharmony_ci phys_size, vma->vm_page_prot)) 103462306a36Sopenharmony_ci return -EAGAIN; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci offset = 0; 103762306a36Sopenharmony_ci size -= phys_size; 103862306a36Sopenharmony_ci addr += phys_size; 103962306a36Sopenharmony_ci tile++; 104062306a36Sopenharmony_ci } while (size); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci return 0; 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic const struct fb_ops gbefb_ops = { 104662306a36Sopenharmony_ci .owner = THIS_MODULE, 104762306a36Sopenharmony_ci .fb_check_var = gbefb_check_var, 104862306a36Sopenharmony_ci .fb_set_par = gbefb_set_par, 104962306a36Sopenharmony_ci .fb_setcolreg = gbefb_setcolreg, 105062306a36Sopenharmony_ci .fb_mmap = gbefb_mmap, 105162306a36Sopenharmony_ci .fb_blank = gbefb_blank, 105262306a36Sopenharmony_ci .fb_fillrect = cfb_fillrect, 105362306a36Sopenharmony_ci .fb_copyarea = cfb_copyarea, 105462306a36Sopenharmony_ci .fb_imageblit = cfb_imageblit, 105562306a36Sopenharmony_ci}; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci/* 105862306a36Sopenharmony_ci * sysfs 105962306a36Sopenharmony_ci */ 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_cistatic ssize_t gbefb_show_memsize(struct device *dev, struct device_attribute *attr, char *buf) 106262306a36Sopenharmony_ci{ 106362306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", gbe_mem_size); 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic DEVICE_ATTR(size, S_IRUGO, gbefb_show_memsize, NULL); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_cistatic ssize_t gbefb_show_rev(struct device *device, struct device_attribute *attr, char *buf) 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", gbe_revision); 107162306a36Sopenharmony_ci} 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_cistatic DEVICE_ATTR(revision, S_IRUGO, gbefb_show_rev, NULL); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_cistatic struct attribute *gbefb_attrs[] = { 107662306a36Sopenharmony_ci &dev_attr_size.attr, 107762306a36Sopenharmony_ci &dev_attr_revision.attr, 107862306a36Sopenharmony_ci NULL, 107962306a36Sopenharmony_ci}; 108062306a36Sopenharmony_ciATTRIBUTE_GROUPS(gbefb); 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci/* 108362306a36Sopenharmony_ci * Initialization 108462306a36Sopenharmony_ci */ 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_cistatic int gbefb_setup(char *options) 108762306a36Sopenharmony_ci{ 108862306a36Sopenharmony_ci char *this_opt; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (!options || !*options) 109162306a36Sopenharmony_ci return 0; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci while ((this_opt = strsep(&options, ",")) != NULL) { 109462306a36Sopenharmony_ci if (!strncmp(this_opt, "monitor:", 8)) { 109562306a36Sopenharmony_ci if (!strncmp(this_opt + 8, "crt", 3)) { 109662306a36Sopenharmony_ci flat_panel_enabled = 0; 109762306a36Sopenharmony_ci default_var = &default_var_CRT; 109862306a36Sopenharmony_ci default_mode = &default_mode_CRT; 109962306a36Sopenharmony_ci } else if (!strncmp(this_opt + 8, "1600sw", 6) || 110062306a36Sopenharmony_ci !strncmp(this_opt + 8, "lcd", 3)) { 110162306a36Sopenharmony_ci flat_panel_enabled = 1; 110262306a36Sopenharmony_ci default_var = &default_var_LCD; 110362306a36Sopenharmony_ci default_mode = &default_mode_LCD; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci } else if (!strncmp(this_opt, "mem:", 4)) { 110662306a36Sopenharmony_ci gbe_mem_size = memparse(this_opt + 4, &this_opt); 110762306a36Sopenharmony_ci if (gbe_mem_size > CONFIG_FB_GBE_MEM * 1024 * 1024) 110862306a36Sopenharmony_ci gbe_mem_size = CONFIG_FB_GBE_MEM * 1024 * 1024; 110962306a36Sopenharmony_ci if (gbe_mem_size < TILE_SIZE) 111062306a36Sopenharmony_ci gbe_mem_size = TILE_SIZE; 111162306a36Sopenharmony_ci } else 111262306a36Sopenharmony_ci mode_option = this_opt; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci return 0; 111562306a36Sopenharmony_ci} 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_cistatic int gbefb_probe(struct platform_device *p_dev) 111862306a36Sopenharmony_ci{ 111962306a36Sopenharmony_ci int i, ret = 0; 112062306a36Sopenharmony_ci struct fb_info *info; 112162306a36Sopenharmony_ci struct gbefb_par *par; 112262306a36Sopenharmony_ci#ifndef MODULE 112362306a36Sopenharmony_ci char *options = NULL; 112462306a36Sopenharmony_ci#endif 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(struct gbefb_par), &p_dev->dev); 112762306a36Sopenharmony_ci if (!info) 112862306a36Sopenharmony_ci return -ENOMEM; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci#ifndef MODULE 113162306a36Sopenharmony_ci if (fb_get_options("gbefb", &options)) { 113262306a36Sopenharmony_ci ret = -ENODEV; 113362306a36Sopenharmony_ci goto out_release_framebuffer; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci gbefb_setup(options); 113662306a36Sopenharmony_ci#endif 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci if (!request_mem_region(GBE_BASE, sizeof(struct sgi_gbe), "GBE")) { 113962306a36Sopenharmony_ci printk(KERN_ERR "gbefb: couldn't reserve mmio region\n"); 114062306a36Sopenharmony_ci ret = -EBUSY; 114162306a36Sopenharmony_ci goto out_release_framebuffer; 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci gbe = (struct sgi_gbe *) devm_ioremap(&p_dev->dev, GBE_BASE, 114562306a36Sopenharmony_ci sizeof(struct sgi_gbe)); 114662306a36Sopenharmony_ci if (!gbe) { 114762306a36Sopenharmony_ci printk(KERN_ERR "gbefb: couldn't map mmio region\n"); 114862306a36Sopenharmony_ci ret = -ENXIO; 114962306a36Sopenharmony_ci goto out_release_mem_region; 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci gbe_revision = gbe->ctrlstat & 15; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci gbe_tiles.cpu = dmam_alloc_coherent(&p_dev->dev, 115462306a36Sopenharmony_ci GBE_TLB_SIZE * sizeof(uint16_t), 115562306a36Sopenharmony_ci &gbe_tiles.dma, GFP_KERNEL); 115662306a36Sopenharmony_ci if (!gbe_tiles.cpu) { 115762306a36Sopenharmony_ci printk(KERN_ERR "gbefb: couldn't allocate tiles table\n"); 115862306a36Sopenharmony_ci ret = -ENOMEM; 115962306a36Sopenharmony_ci goto out_release_mem_region; 116062306a36Sopenharmony_ci } 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci if (gbe_mem_phys) { 116362306a36Sopenharmony_ci /* memory was allocated at boot time */ 116462306a36Sopenharmony_ci gbe_mem = devm_ioremap_wc(&p_dev->dev, gbe_mem_phys, 116562306a36Sopenharmony_ci gbe_mem_size); 116662306a36Sopenharmony_ci if (!gbe_mem) { 116762306a36Sopenharmony_ci printk(KERN_ERR "gbefb: couldn't map framebuffer\n"); 116862306a36Sopenharmony_ci ret = -ENOMEM; 116962306a36Sopenharmony_ci goto out_release_mem_region; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci gbe_dma_addr = 0; 117362306a36Sopenharmony_ci } else { 117462306a36Sopenharmony_ci /* try to allocate memory with the classical allocator 117562306a36Sopenharmony_ci * this has high chance to fail on low memory machines */ 117662306a36Sopenharmony_ci gbe_mem = dmam_alloc_attrs(&p_dev->dev, gbe_mem_size, 117762306a36Sopenharmony_ci &gbe_dma_addr, GFP_KERNEL, 117862306a36Sopenharmony_ci DMA_ATTR_WRITE_COMBINE); 117962306a36Sopenharmony_ci if (!gbe_mem) { 118062306a36Sopenharmony_ci printk(KERN_ERR "gbefb: couldn't allocate framebuffer memory\n"); 118162306a36Sopenharmony_ci ret = -ENOMEM; 118262306a36Sopenharmony_ci goto out_release_mem_region; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci gbe_mem_phys = (unsigned long) gbe_dma_addr; 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci par = info->par; 118962306a36Sopenharmony_ci par->wc_cookie = arch_phys_wc_add(gbe_mem_phys, gbe_mem_size); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci /* map framebuffer memory into tiles table */ 119262306a36Sopenharmony_ci for (i = 0; i < (gbe_mem_size >> TILE_SHIFT); i++) 119362306a36Sopenharmony_ci gbe_tiles.cpu[i] = (gbe_mem_phys >> TILE_SHIFT) + i; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci info->fbops = &gbefb_ops; 119662306a36Sopenharmony_ci info->pseudo_palette = pseudo_palette; 119762306a36Sopenharmony_ci info->screen_base = gbe_mem; 119862306a36Sopenharmony_ci fb_alloc_cmap(&info->cmap, 256, 0); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci /* reset GBE */ 120162306a36Sopenharmony_ci gbe_reset(); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci /* turn on default video mode */ 120462306a36Sopenharmony_ci if (fb_find_mode(&par->var, info, mode_option, NULL, 0, 120562306a36Sopenharmony_ci default_mode, 8) == 0) 120662306a36Sopenharmony_ci par->var = *default_var; 120762306a36Sopenharmony_ci info->var = par->var; 120862306a36Sopenharmony_ci gbefb_check_var(&par->var, info); 120962306a36Sopenharmony_ci gbefb_encode_fix(&info->fix, &info->var); 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci if (register_framebuffer(info) < 0) { 121262306a36Sopenharmony_ci printk(KERN_ERR "gbefb: couldn't register framebuffer\n"); 121362306a36Sopenharmony_ci ret = -ENXIO; 121462306a36Sopenharmony_ci goto out_gbe_unmap; 121562306a36Sopenharmony_ci } 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci platform_set_drvdata(p_dev, info); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci fb_info(info, "%s rev %d @ 0x%08x using %dkB memory\n", 122062306a36Sopenharmony_ci info->fix.id, gbe_revision, (unsigned)GBE_BASE, 122162306a36Sopenharmony_ci gbe_mem_size >> 10); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci return 0; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ciout_gbe_unmap: 122662306a36Sopenharmony_ci arch_phys_wc_del(par->wc_cookie); 122762306a36Sopenharmony_ciout_release_mem_region: 122862306a36Sopenharmony_ci release_mem_region(GBE_BASE, sizeof(struct sgi_gbe)); 122962306a36Sopenharmony_ciout_release_framebuffer: 123062306a36Sopenharmony_ci framebuffer_release(info); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci return ret; 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_cistatic void gbefb_remove(struct platform_device* p_dev) 123662306a36Sopenharmony_ci{ 123762306a36Sopenharmony_ci struct fb_info *info = platform_get_drvdata(p_dev); 123862306a36Sopenharmony_ci struct gbefb_par *par = info->par; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci unregister_framebuffer(info); 124162306a36Sopenharmony_ci gbe_turn_off(); 124262306a36Sopenharmony_ci arch_phys_wc_del(par->wc_cookie); 124362306a36Sopenharmony_ci release_mem_region(GBE_BASE, sizeof(struct sgi_gbe)); 124462306a36Sopenharmony_ci framebuffer_release(info); 124562306a36Sopenharmony_ci} 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_cistatic struct platform_driver gbefb_driver = { 124862306a36Sopenharmony_ci .probe = gbefb_probe, 124962306a36Sopenharmony_ci .remove_new = gbefb_remove, 125062306a36Sopenharmony_ci .driver = { 125162306a36Sopenharmony_ci .name = "gbefb", 125262306a36Sopenharmony_ci .dev_groups = gbefb_groups, 125362306a36Sopenharmony_ci }, 125462306a36Sopenharmony_ci}; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_cistatic struct platform_device *gbefb_device; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_cistatic int __init gbefb_init(void) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci int ret = platform_driver_register(&gbefb_driver); 126162306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SGI_IP32) && !ret) { 126262306a36Sopenharmony_ci gbefb_device = platform_device_alloc("gbefb", 0); 126362306a36Sopenharmony_ci if (gbefb_device) { 126462306a36Sopenharmony_ci ret = platform_device_add(gbefb_device); 126562306a36Sopenharmony_ci } else { 126662306a36Sopenharmony_ci ret = -ENOMEM; 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci if (ret) { 126962306a36Sopenharmony_ci platform_device_put(gbefb_device); 127062306a36Sopenharmony_ci platform_driver_unregister(&gbefb_driver); 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci return ret; 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_cistatic void __exit gbefb_exit(void) 127762306a36Sopenharmony_ci{ 127862306a36Sopenharmony_ci platform_device_unregister(gbefb_device); 127962306a36Sopenharmony_ci platform_driver_unregister(&gbefb_driver); 128062306a36Sopenharmony_ci} 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_cimodule_init(gbefb_init); 128362306a36Sopenharmony_cimodule_exit(gbefb_exit); 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1286