1287 lines · 31.9 KB · C
RawDownload
/* *  SGI GBE frame buffer driver * *  Copyright (C) 1999 Silicon Graphics, Inc. - Jeffrey Newquist *  Copyright (C) 2002 Vivien Chappelier <vivien.chappelier@linux-mips.org> * *  This file is subject to the terms and conditions of the GNU General Public *  License. See the file COPYING in the main directory of this archive for *  more details. */ #include <linux/delay.h>#include <linux/platform_device.h>#include <linux/dma-mapping.h>#include <linux/dma-direct.h>#include <linux/errno.h>#include <linux/gfp.h>#include <linux/fb.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/module.h>#include <linux/io.h> #ifdef CONFIG_MIPS#include <asm/addrspace.h>#endif#include <asm/byteorder.h>#include <asm/tlbflush.h> #include <video/gbe.h> static struct sgi_gbe *gbe; struct gbefb_par {	struct fb_var_screeninfo var;	struct gbe_timing_info timing;	int wc_cookie;	int valid;}; #define GBE_BASE	0x16000000 /* SGI O2 */ /* macro for fastest write-though access to the framebuffer */#ifdef CONFIG_MIPS#ifdef CONFIG_CPU_R10000#define pgprot_fb(_prot) (((_prot) & (~_CACHE_MASK)) | _CACHE_UNCACHED_ACCELERATED)#else#define pgprot_fb(_prot) (((_prot) & (~_CACHE_MASK)) | _CACHE_CACHABLE_NO_WA)#endif#endif /* *  RAM we reserve for the frame buffer. This defines the maximum screen *  size */#if CONFIG_FB_GBE_MEM > 8#error GBE Framebuffer cannot use more than 8MB of memory#endif #define TILE_SHIFT 16#define TILE_SIZE (1 << TILE_SHIFT)#define TILE_MASK (TILE_SIZE - 1) static unsigned int gbe_mem_size = CONFIG_FB_GBE_MEM * 1024*1024;static void *gbe_mem;static dma_addr_t gbe_dma_addr;static phys_addr_t gbe_mem_phys; static struct {	uint16_t *cpu;	dma_addr_t dma;} gbe_tiles; static int gbe_revision; static int ypan, ywrap; static uint32_t pseudo_palette[16];static uint32_t gbe_cmap[256];static int gbe_turned_on; /* 0 turned off, 1 turned on */ static char *mode_option = NULL; /* default CRT mode */static struct fb_var_screeninfo default_var_CRT = {	/* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */	.xres		= 640,	.yres		= 480,	.xres_virtual	= 640,	.yres_virtual	= 480,	.xoffset	= 0,	.yoffset	= 0,	.bits_per_pixel	= 8,	.grayscale	= 0,	.red		= { 0, 8, 0 },	.green		= { 0, 8, 0 },	.blue		= { 0, 8, 0 },	.transp		= { 0, 0, 0 },	.nonstd		= 0,	.activate	= 0,	.height		= -1,	.width		= -1,	.accel_flags	= 0,	.pixclock	= 39722,	/* picoseconds */	.left_margin	= 48,	.right_margin	= 16,	.upper_margin	= 33,	.lower_margin	= 10,	.hsync_len	= 96,	.vsync_len	= 2,	.sync		= 0,	.vmode		= FB_VMODE_NONINTERLACED,}; /* default LCD mode */static struct fb_var_screeninfo default_var_LCD = {	/* 1600x1024, 8 bpp */	.xres		= 1600,	.yres		= 1024,	.xres_virtual	= 1600,	.yres_virtual	= 1024,	.xoffset	= 0,	.yoffset	= 0,	.bits_per_pixel	= 8,	.grayscale	= 0,	.red		= { 0, 8, 0 },	.green		= { 0, 8, 0 },	.blue		= { 0, 8, 0 },	.transp		= { 0, 0, 0 },	.nonstd		= 0,	.activate	= 0,	.height		= -1,	.width		= -1,	.accel_flags	= 0,	.pixclock	= 9353,	.left_margin	= 20,	.right_margin	= 30,	.upper_margin	= 37,	.lower_margin	= 3,	.hsync_len	= 20,	.vsync_len	= 3,	.sync		= 0,	.vmode		= FB_VMODE_NONINTERLACED}; /* default modedb mode *//* 640x480, 60 Hz, Non-Interlaced (25.172 MHz dotclock) */static struct fb_videomode default_mode_CRT = {	.refresh	= 60,	.xres		= 640,	.yres		= 480,	.pixclock	= 39722,	.left_margin	= 48,	.right_margin	= 16,	.upper_margin	= 33,	.lower_margin	= 10,	.hsync_len	= 96,	.vsync_len	= 2,	.sync		= 0,	.vmode		= FB_VMODE_NONINTERLACED,};/* 1600x1024 SGI flatpanel 1600sw */static struct fb_videomode default_mode_LCD = {	/* 1600x1024, 8 bpp */	.xres		= 1600,	.yres		= 1024,	.pixclock	= 9353,	.left_margin	= 20,	.right_margin	= 30,	.upper_margin	= 37,	.lower_margin	= 3,	.hsync_len	= 20,	.vsync_len	= 3,	.vmode		= FB_VMODE_NONINTERLACED,}; static struct fb_videomode *default_mode = &default_mode_CRT;static struct fb_var_screeninfo *default_var = &default_var_CRT; static int flat_panel_enabled = 0; static void gbe_reset(void){	/* Turn on dotclock PLL */	gbe->ctrlstat = 0x300aa000;}  /* * Function:	gbe_turn_off * Parameters:	(None) * Description:	This should turn off the monitor and gbe.  This is used *              when switching between the serial console and the graphics *              console. */ static void gbe_turn_off(void){	int i;	unsigned int val, y, vpixen_off; 	gbe_turned_on = 0; 	/* check if pixel counter is on */	val = gbe->vt_xy;	if (GET_GBE_FIELD(VT_XY, FREEZE, val) == 1)		return; 	/* turn off DMA */	val = gbe->ovr_control;	SET_GBE_FIELD(OVR_CONTROL, OVR_DMA_ENABLE, val, 0);	gbe->ovr_control = val;	udelay(1000);	val = gbe->frm_control;	SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 0);	gbe->frm_control = val;	udelay(1000);	val = gbe->did_control;	SET_GBE_FIELD(DID_CONTROL, DID_DMA_ENABLE, val, 0);	gbe->did_control = val;	udelay(1000); 	/* We have to wait through two vertical retrace periods before	 * the pixel DMA is turned off for sure. */	for (i = 0; i < 10000; i++) {		val = gbe->frm_inhwctrl;		if (GET_GBE_FIELD(FRM_INHWCTRL, FRM_DMA_ENABLE, val)) {			udelay(10);		} else {			val = gbe->ovr_inhwctrl;			if (GET_GBE_FIELD(OVR_INHWCTRL, OVR_DMA_ENABLE, val)) {				udelay(10);			} else {				val = gbe->did_inhwctrl;				if (GET_GBE_FIELD(DID_INHWCTRL, DID_DMA_ENABLE, val)) {					udelay(10);				} else					break;			}		}	}	if (i == 10000)		printk(KERN_ERR "gbefb: turn off DMA timed out\n"); 	/* wait for vpixen_off */	val = gbe->vt_vpixen;	vpixen_off = GET_GBE_FIELD(VT_VPIXEN, VPIXEN_OFF, val); 	for (i = 0; i < 100000; i++) {		val = gbe->vt_xy;		y = GET_GBE_FIELD(VT_XY, Y, val);		if (y < vpixen_off)			break;		udelay(1);	}	if (i == 100000)		printk(KERN_ERR		       "gbefb: wait for vpixen_off timed out\n");	for (i = 0; i < 10000; i++) {		val = gbe->vt_xy;		y = GET_GBE_FIELD(VT_XY, Y, val);		if (y > vpixen_off)			break;		udelay(1);	}	if (i == 10000)		printk(KERN_ERR "gbefb: wait for vpixen_off timed out\n"); 	/* turn off pixel counter */	val = 0;	SET_GBE_FIELD(VT_XY, FREEZE, val, 1);	gbe->vt_xy = val;	mdelay(10);	for (i = 0; i < 10000; i++) {		val = gbe->vt_xy;		if (GET_GBE_FIELD(VT_XY, FREEZE, val) != 1)			udelay(10);		else			break;	}	if (i == 10000)		printk(KERN_ERR "gbefb: turn off pixel clock timed out\n"); 	/* turn off dot clock */	val = gbe->dotclock;	SET_GBE_FIELD(DOTCLK, RUN, val, 0);	gbe->dotclock = val;	mdelay(10);	for (i = 0; i < 10000; i++) {		val = gbe->dotclock;		if (GET_GBE_FIELD(DOTCLK, RUN, val))			udelay(10);		else			break;	}	if (i == 10000)		printk(KERN_ERR "gbefb: turn off dotclock timed out\n"); 	/* reset the frame DMA FIFO */	val = gbe->frm_size_tile;	SET_GBE_FIELD(FRM_SIZE_TILE, FRM_FIFO_RESET, val, 1);	gbe->frm_size_tile = val;	SET_GBE_FIELD(FRM_SIZE_TILE, FRM_FIFO_RESET, val, 0);	gbe->frm_size_tile = val;} static void gbe_turn_on(void){	unsigned int val, i; 	/*	 * Check if pixel counter is off, for unknown reason this	 * code hangs Visual Workstations	 */	if (gbe_revision < 2) {		val = gbe->vt_xy;		if (GET_GBE_FIELD(VT_XY, FREEZE, val) == 0)			return;	} 	/* turn on dot clock */	val = gbe->dotclock;	SET_GBE_FIELD(DOTCLK, RUN, val, 1);	gbe->dotclock = val;	mdelay(10);	for (i = 0; i < 10000; i++) {		val = gbe->dotclock;		if (GET_GBE_FIELD(DOTCLK, RUN, val) != 1)			udelay(10);		else			break;	}	if (i == 10000)		printk(KERN_ERR "gbefb: turn on dotclock timed out\n"); 	/* turn on pixel counter */	val = 0;	SET_GBE_FIELD(VT_XY, FREEZE, val, 0);	gbe->vt_xy = val;	mdelay(10);	for (i = 0; i < 10000; i++) {		val = gbe->vt_xy;		if (GET_GBE_FIELD(VT_XY, FREEZE, val))			udelay(10);		else			break;	}	if (i == 10000)		printk(KERN_ERR "gbefb: turn on pixel clock timed out\n"); 	/* turn on DMA */	val = gbe->frm_control;	SET_GBE_FIELD(FRM_CONTROL, FRM_DMA_ENABLE, val, 1);	gbe->frm_control = val;	udelay(1000);	for (i = 0; i < 10000; i++) {		val = gbe->frm_inhwctrl;		if (GET_GBE_FIELD(FRM_INHWCTRL, FRM_DMA_ENABLE, val) != 1)			udelay(10);		else			break;	}	if (i == 10000)		printk(KERN_ERR "gbefb: turn on DMA timed out\n"); 	gbe_turned_on = 1;} static void gbe_loadcmap(void){	int i, j; 	for (i = 0; i < 256; i++) {		for (j = 0; j < 1000 && gbe->cm_fifo >= 63; j++)			udelay(10);		if (j == 1000)			printk(KERN_ERR "gbefb: cmap FIFO timeout\n"); 		gbe->cmap[i] = gbe_cmap[i];	}} /* *  Blank the display. */static int gbefb_blank(int blank, struct fb_info *info){	/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */	switch (blank) {	case FB_BLANK_UNBLANK:		/* unblank */		gbe_turn_on();		gbe_loadcmap();		break; 	case FB_BLANK_NORMAL:		/* blank */		gbe_turn_off();		break; 	default:		/* Nothing */		break;	}	return 0;} /* *  Setup flatpanel related registers. */static void gbefb_setup_flatpanel(struct gbe_timing_info *timing){	int fp_wid, fp_hgt, fp_vbs, fp_vbe;	u32 outputVal = 0; 	SET_GBE_FIELD(VT_FLAGS, HDRV_INVERT, outputVal,		(timing->flags & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1);	SET_GBE_FIELD(VT_FLAGS, VDRV_INVERT, outputVal,		(timing->flags & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1);	gbe->vt_flags = outputVal; 	/* Turn on the flat panel */	fp_wid = 1600;	fp_hgt = 1024;	fp_vbs = 0;	fp_vbe = 1600;	timing->pll_m = 4;	timing->pll_n = 1;	timing->pll_p = 0; 	outputVal = 0;	SET_GBE_FIELD(FP_DE, ON, outputVal, fp_vbs);	SET_GBE_FIELD(FP_DE, OFF, outputVal, fp_vbe);	gbe->fp_de = outputVal;	outputVal = 0;	SET_GBE_FIELD(FP_HDRV, OFF, outputVal, fp_wid);	gbe->fp_hdrv = outputVal;	outputVal = 0;	SET_GBE_FIELD(FP_VDRV, ON, outputVal, 1);	SET_GBE_FIELD(FP_VDRV, OFF, outputVal, fp_hgt + 1);	gbe->fp_vdrv = outputVal;} struct gbe_pll_info {	int clock_rate;	int fvco_min;	int fvco_max;}; static struct gbe_pll_info gbe_pll_table[2] = {	{ 20, 80, 220 },	{ 27, 80, 220 },}; static int compute_gbe_timing(struct fb_var_screeninfo *var,			      struct gbe_timing_info *timing){	int pll_m, pll_n, pll_p, error, best_m, best_n, best_p, best_error;	int pixclock;	struct gbe_pll_info *gbe_pll; 	if (gbe_revision < 2)		gbe_pll = &gbe_pll_table[0];	else		gbe_pll = &gbe_pll_table[1]; 	/* Determine valid resolution and timing	 * GBE crystal runs at 20Mhz or 27Mhz	 * pll_m, pll_n, pll_p define the following frequencies	 * fvco = pll_m * 20Mhz / pll_n	 * fout = fvco / (2**pll_p) */	best_error = 1000000000;	best_n = best_m = best_p = 0;	for (pll_p = 0; pll_p < 4; pll_p++)		for (pll_m = 1; pll_m < 256; pll_m++)			for (pll_n = 1; pll_n < 64; pll_n++) {				pixclock = (1000000 / gbe_pll->clock_rate) *						(pll_n << pll_p) / pll_m; 				error = var->pixclock - pixclock; 				if (error < 0)					error = -error; 				if (error < best_error &&				    pll_m / pll_n >				    gbe_pll->fvco_min / gbe_pll->clock_rate && 				    pll_m / pll_n <				    gbe_pll->fvco_max / gbe_pll->clock_rate) {					best_error = error;					best_m = pll_m;					best_n = pll_n;					best_p = pll_p;				}			} 	if (!best_n || !best_m)		return -EINVAL;	/* Resolution to high */ 	pixclock = (1000000 / gbe_pll->clock_rate) *

Lines 1–500 of 1287.