⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sstfb.c

📁 Linux环境下视频显示卡设备的驱动程序源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * linux/drivers/video/sstfb.c -- voodoo graphics frame buffer * *     Copyright (c) 2000-2002 Ghozlane Toumi <gtoumi@laposte.net> * *     Created 15 Jan 2000 by Ghozlane Toumi * * Contributions (and many thanks) : * * 03/2001 James Simmons   <jsimmons@infradead.org> * 04/2001 Paul Mundt      <lethal@chaoticdreams.org> * 05/2001 Urs Ganse       <ursg@uni.de> *	(initial work on voodoo2 port, interlace) * 09/2002 Helge Deller    <deller@gmx.de> *	(enable driver on big-endian machines (hppa), ioctl fixes) * 12/2002 Helge Deller    <deller@gmx.de> *	(port driver to new frambuffer infrastructure) * 01/2003 Helge Deller    <deller@gmx.de> *	(initial work on fb hardware acceleration for voodoo2) * 08/2006 Alan Cox 	   <alan@redhat.com> *	Remove never finished and bogus 24/32bit support *	Clean up macro abuse *	Minor tidying for format. * 12/2006 Helge Deller    <deller@gmx.de> *	add /sys/class/graphics/fbX/vgapass sysfs-interface *	add module option "mode_option" to set initial screen mode *	use fbdev default videomode database *	remove debug functions from ioctl *//* * The voodoo1 has the following memory mapped address space: * 0x000000 - 0x3fffff : registers              (4MB) * 0x400000 - 0x7fffff : linear frame buffer    (4MB) * 0x800000 - 0xffffff : texture memory         (8MB) *//* * misc notes, TODOs, toASKs, and deep thoughts-TODO: at one time or another test that the mode is acceptable by the monitor-ASK: Can I choose different ordering for the color bitfields (rgba argb ...)      which one should i use ? is there any preferred one ? It seems ARGB is      the one ...-TODO: in  set_var check the validity of timings (hsync vsync)...-TODO: check and recheck the use of sst_wait_idle : we don't flush the fifo via       a nop command. so it's ok as long as the commands we pass don't go       through the fifo. warning: issuing a nop command seems to need pci_fifo-FIXME: in case of failure in the init sequence, be sure we return to a safe        state.- FIXME: Use accelerator for 2D scroll-FIXME: 4MB boards have banked memory (FbiInit2 bits 1 & 20) *//* * debug info * SST_DEBUG : enable debugging * SST_DEBUG_REG : debug registers *   0 :  no debug *   1 : dac calls, [un]set_bits, FbiInit *   2 : insane debug level (log every register read/write) * SST_DEBUG_FUNC : functions *   0 : no debug *   1 : function call / debug ioctl *   2 : variables *   3 : flood . you don't want to do that. trust me. * SST_DEBUG_VAR : debug display/var structs *   0 : no debug *   1 : dumps display, fb_var * * sstfb specific ioctls: *   		toggle vga (0x46db) : toggle vga_pass_through */#undef SST_DEBUG/* * Includes */#include <linux/string.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/fb.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/slab.h>#include <asm/io.h>#include <linux/uaccess.h>#include <video/sstfb.h>/* initialized by setup */static int vgapass;		/* enable VGA passthrough cable */static int mem;			/* mem size in MB, 0 = autodetect */static int clipping = 1;	/* use clipping (slower, safer) */static int gfxclk;		/* force FBI freq in Mhz . Dangerous */static int slowpci;		/* slow PCI settings *//*  Possible default video modes: 800x600@60, 640x480@75, 1024x768@76, 640x480@60*/#define DEFAULT_VIDEO_MODE "640x480@60"static char *mode_option __devinitdata = DEFAULT_VIDEO_MODE;enum {	ID_VOODOO1 = 0,	ID_VOODOO2 = 1,};#define IS_VOODOO2(par) ((par)->type == ID_VOODOO2)static struct sst_spec voodoo_spec[] __devinitdata = { { .name = "Voodoo Graphics", .default_gfx_clock = 50000, .max_gfxclk = 60 }, { .name = "Voodoo2",	      .default_gfx_clock = 75000, .max_gfxclk = 85 },};/* * debug functions */#if (SST_DEBUG_REG > 0)static void sst_dbg_print_read_reg(u32 reg, u32 val) {	const char *regname;	switch (reg) {	case FBIINIT0:	regname = "FbiInit0"; break;	case FBIINIT1:	regname = "FbiInit1"; break;	case FBIINIT2:	regname = "FbiInit2"; break;	case FBIINIT3:	regname = "FbiInit3"; break;	case FBIINIT4:	regname = "FbiInit4"; break;	case FBIINIT5:	regname = "FbiInit5"; break;	case FBIINIT6:	regname = "FbiInit6"; break;	default:	regname = NULL;       break;	}	if (regname == NULL)		r_ddprintk("sst_read(%#x): %#x\n", reg, val);	else		r_dprintk(" sst_read(%s): %#x\n", regname, val);}static void sst_dbg_print_write_reg(u32 reg, u32 val) {	const char *regname;	switch (reg) {	case FBIINIT0:	regname = "FbiInit0"; break;	case FBIINIT1:	regname = "FbiInit1"; break;	case FBIINIT2:	regname = "FbiInit2"; break;	case FBIINIT3:	regname = "FbiInit3"; break;	case FBIINIT4:	regname = "FbiInit4"; break;	case FBIINIT5:	regname = "FbiInit5"; break;	case FBIINIT6:	regname = "FbiInit6"; break;	default:	regname = NULL;       break;	}	if (regname == NULL)		r_ddprintk("sst_write(%#x, %#x)\n", reg, val);	else		r_dprintk(" sst_write(%s, %#x)\n", regname, val);}#else /*  (SST_DEBUG_REG > 0) */#  define sst_dbg_print_read_reg(reg, val)	do {} while(0)#  define sst_dbg_print_write_reg(reg, val)	do {} while(0)#endif /*  (SST_DEBUG_REG > 0) *//* * hardware access functions *//* register access */#define sst_read(reg)		__sst_read(par->mmio_vbase, reg)#define sst_write(reg,val)	__sst_write(par->mmio_vbase, reg, val)#define sst_set_bits(reg,val)	__sst_set_bits(par->mmio_vbase, reg, val)#define sst_unset_bits(reg,val)	__sst_unset_bits(par->mmio_vbase, reg, val)#define sst_dac_read(reg)	__sst_dac_read(par->mmio_vbase, reg)#define sst_dac_write(reg,val)	__sst_dac_write(par->mmio_vbase, reg, val)#define dac_i_read(reg)		__dac_i_read(par->mmio_vbase, reg)#define dac_i_write(reg,val)	__dac_i_write(par->mmio_vbase, reg, val)static inline u32 __sst_read(u8 __iomem *vbase, u32 reg){	u32 ret = readl(vbase + reg);	sst_dbg_print_read_reg(reg, ret);	return ret;}static inline void __sst_write(u8 __iomem *vbase, u32 reg, u32 val){	sst_dbg_print_write_reg(reg, val);	writel(val, vbase + reg);}static inline void __sst_set_bits(u8 __iomem *vbase, u32 reg, u32 val){	r_dprintk("sst_set_bits(%#x, %#x)\n", reg, val);	__sst_write(vbase, reg, __sst_read(vbase, reg) | val);}static inline void __sst_unset_bits(u8 __iomem *vbase, u32 reg, u32 val){	r_dprintk("sst_unset_bits(%#x, %#x)\n", reg, val);	__sst_write(vbase, reg, __sst_read(vbase, reg) & ~val);}/* * wait for the fbi chip. ASK: what happens if the fbi is stuck ? * * the FBI is supposed to be ready if we receive 5 time * in a row a "idle" answer to our requests */#define sst_wait_idle() __sst_wait_idle(par->mmio_vbase)static int __sst_wait_idle(u8 __iomem *vbase){	int count = 0;	/* if (doFBINOP) __sst_write(vbase, NOPCMD, 0); */	while(1) {		if (__sst_read(vbase, STATUS) & STATUS_FBI_BUSY) {			f_dddprintk("status: busy\n");/* FIXME basicaly, this is a busy wait. maybe not that good. oh well; * this is a small loop after all. * Or maybe we should use mdelay() or udelay() here instead ? */			count = 0;		} else {			count++;			f_dddprintk("status: idle(%d)\n", count);		}		if (count >= 5) return 1;/* XXX  do something to avoid hanging the machine if the voodoo is out */	}}/* dac access *//* dac_read should be remaped to FbiInit2 (via the pci reg init_enable) */static u8 __sst_dac_read(u8 __iomem *vbase, u8 reg){	u8 ret;	reg &= 0x07;	__sst_write(vbase, DAC_DATA, ((u32)reg << 8) | DAC_READ_CMD );	__sst_wait_idle(vbase);	/* udelay(10); */	ret = __sst_read(vbase, DAC_READ) & 0xff;	r_dprintk("sst_dac_read(%#x): %#x\n", reg, ret);	return ret;}static void __sst_dac_write(u8 __iomem *vbase, u8 reg, u8 val){	r_dprintk("sst_dac_write(%#x, %#x)\n", reg, val);	reg &= 0x07;	__sst_write(vbase, DAC_DATA,(((u32)reg << 8)) | (u32)val);	__sst_wait_idle(vbase);}/* indexed access to ti/att dacs */static u32 __dac_i_read(u8 __iomem *vbase, u8 reg){	u32 ret;	__sst_dac_write(vbase, DACREG_ADDR_I, reg);	ret = __sst_dac_read(vbase, DACREG_DATA_I);	r_dprintk("sst_dac_read_i(%#x): %#x\n", reg, ret);	return ret;}static void __dac_i_write(u8 __iomem *vbase, u8 reg,u8 val){	r_dprintk("sst_dac_write_i(%#x, %#x)\n", reg, val);	__sst_dac_write(vbase, DACREG_ADDR_I, reg);	__sst_dac_write(vbase, DACREG_DATA_I, val);}/* compute the m,n,p  , returns the real freq * (ics datasheet :  N <-> N1 , P <-> N2) * * Fout= Fref * (M+2)/( 2^P * (N+2)) *  we try to get close to the asked freq *  with P as high, and M as low as possible * range: * ti/att : 0 <= M <= 255; 0 <= P <= 3; 0<= N <= 63 * ics    : 1 <= M <= 127; 0 <= P <= 3; 1<= N <= 31 * we'll use the lowest limitation, should be precise enouth */static int sst_calc_pll(const int freq, int *freq_out, struct pll_timing *t){	int m, m2, n, p, best_err, fout;	int best_n = -1;	int best_m = -1;	best_err = freq;	p = 3;	/* f * 2^P = vco should be less than VCOmax ~ 250 MHz for ics*/	while (((1 << p) * freq > VCO_MAX) && (p >= 0))		p--;	if (p == -1)		return -EINVAL;	for (n = 1; n < 32; n++) {		/* calc 2 * m so we can round it later*/		m2 = (2 * freq * (1 << p) * (n + 2) ) / DAC_FREF - 4 ;		m = (m2 % 2 ) ? m2/2+1 : m2/2 ;		if (m >= 128)			break;		fout = (DAC_FREF * (m + 2)) / ((1 << p) * (n + 2));		if ((abs(fout - freq) < best_err) && (m > 0)) {			best_n = n;			best_m = m;			best_err = abs(fout - freq);			/* we get the lowest m , allowing 0.5% error in freq*/			if (200*best_err < freq) break;		}	}	if (best_n == -1)  /* unlikely, but who knows ? */		return -EINVAL;	t->p = p;	t->n = best_n;	t->m = best_m;	*freq_out = (DAC_FREF * (t->m + 2)) / ((1 << t->p) * (t->n + 2));	f_ddprintk ("m: %d, n: %d, p: %d, F: %dKhz\n",		  t->m, t->n, t->p, *freq_out);	return 0;}/* * clear lfb screen */static void sstfb_clear_screen(struct fb_info *info){	/* clear screen */	fb_memset(info->screen_base, 0, info->fix.smem_len);}/** *      sstfb_check_var - Optional function.  Validates a var passed in. *      @var: frame buffer variable screen structure *      @info: frame buffer structure that represents a single frame buffer * *	Limit to the abilities of a single chip as SLI is not supported *	by this driver. */static int sstfb_check_var(struct fb_var_screeninfo *var,		struct fb_info *info){	struct sstfb_par *par = info->par;	int hSyncOff   = var->xres + var->right_margin + var->left_margin;	int vSyncOff   = var->yres + var->lower_margin + var->upper_margin;	int vBackPorch = var->left_margin, yDim = var->yres;	int vSyncOn    = var->vsync_len;	int tiles_in_X, real_length;	unsigned int freq;	if (sst_calc_pll(PICOS2KHZ(var->pixclock), &freq, &par->pll)) {		printk(KERN_ERR "sstfb: Pixclock at %ld KHZ out of range\n",				PICOS2KHZ(var->pixclock));		return -EINVAL;	}	var->pixclock = KHZ2PICOS(freq);		if (var->vmode & FB_VMODE_INTERLACED)		vBackPorch += (vBackPorch % 2);	if (var->vmode & FB_VMODE_DOUBLE) {		vBackPorch <<= 1;		yDim <<=1;		vSyncOn <<=1;		vSyncOff <<=1;	}	switch (var->bits_per_pixel) {	case 0 ... 16 :		var->bits_per_pixel = 16;		break;	default :		printk(KERN_ERR "sstfb: Unsupported bpp %d\n", var->bits_per_pixel);		return -EINVAL;	}		/* validity tests */	if (var->xres <= 1 || yDim <= 0 || var->hsync_len <= 1  ||	    hSyncOff <= 1  || var->left_margin <= 2  || vSyncOn <= 0 ||	    vSyncOff <= 0 || vBackPorch <= 0) {		return -EINVAL;	}	if (IS_VOODOO2(par)) {		/* Voodoo 2 limits */		tiles_in_X = (var->xres + 63 ) / 64 * 2;				if (var->xres  > POW2(11) || yDim >= POW2(11)) {			printk(KERN_ERR "sstfb: Unsupported resolution %dx%d\n",			         var->xres, var->yres);			return -EINVAL;		}		if (var->hsync_len > POW2(9) || hSyncOff > POW2(11) ||		    var->left_margin - 2 >= POW2(9) || vSyncOn >= POW2(13) ||		    vSyncOff >= POW2(13) || vBackPorch >= POW2(9) ||		    tiles_in_X >= POW2(6) || tiles_in_X <= 0) {			printk(KERN_ERR "sstfb: Unsupported timings\n");			return -EINVAL;		}	} else {		/* Voodoo limits */		tiles_in_X = (var->xres + 63 ) / 64;		if (var->vmode) {			printk(KERN_ERR "sstfb: Interlace/doublescan not supported %#x\n",				var->vmode);			return -EINVAL;		}		if (var->xres > POW2(10) || var->yres >= POW2(10)) {			printk(KERN_ERR "sstfb: Unsupported resolution %dx%d\n",			         var->xres, var->yres);			return -EINVAL;		}		if (var->hsync_len > POW2(8) || hSyncOff - 1 > POW2(10) ||		    var->left_margin - 2 >= POW2(8) || vSyncOn >= POW2(12) ||		    vSyncOff >= POW2(12) || vBackPorch >= POW2(8) ||		    tiles_in_X >= POW2(4) || tiles_in_X <= 0) {			printk(KERN_ERR "sstfb: Unsupported timings\n");			return -EINVAL;		}	}	/* it seems that the fbi uses tiles of 64x16 pixels to "map" the mem */	/* FIXME: i don't like this... looks wrong */	real_length = tiles_in_X  * (IS_VOODOO2(par) ? 32 : 64 )	              * ((var->bits_per_pixel == 16) ? 2 : 4);	if (real_length * yDim > info->fix.smem_len) {		printk(KERN_ERR "sstfb: Not enough video memory\n");		return -ENOMEM;	}	var->sync &= (FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT);	var->vmode &= (FB_VMODE_INTERLACED | FB_VMODE_DOUBLE);	var->xoffset = 0;	var->yoffset = 0;	var->height  = -1;	var->width   = -1;	/*	 * correct the color bit fields	 */	/* var->{red|green|blue}.msb_right = 0; */	switch (var->bits_per_pixel) {	case 16:	/* RGB 565  LfbMode 0 */		var->red.length    = 5;		var->green.length  = 6;		var->blue.length   = 5;		var->transp.length = 0;		var->red.offset    = 11;		var->green.offset  = 5;		var->blue.offset   = 0;		var->transp.offset = 0;		break;	default:		return -EINVAL;	}	return 0;}/** *      sstfb_set_par - Optional function.  Alters the hardware state. *      @info: frame buffer structure that represents a single frame buffer */static int sstfb_set_par(struct fb_info *info){	struct sstfb_par *par = info->par;	u32 lfbmode, fbiinit1, fbiinit2, fbiinit3, fbiinit5, fbiinit6=0;	struct pci_dev *sst_dev = par->dev;	unsigned int freq;	int ntiles;	par->hSyncOff	= info->var.xres + info->var.right_margin + info->var.left_margin;	par->yDim 	= info->var.yres;	par->vSyncOn 	= info->var.vsync_len;	par->vSyncOff	= info->var.yres + info->var.lower_margin + info->var.upper_margin;	par->vBackPorch = info->var.upper_margin;	/* We need par->pll */	sst_calc_pll(PICOS2KHZ(info->var.pixclock), &freq, &par->pll);	if (info->var.vmode & FB_VMODE_INTERLACED)		par->vBackPorch += (par->vBackPorch % 2);	if (info->var.vmode & FB_VMODE_DOUBLE) {		par->vBackPorch <<= 1;		par->yDim <<=1;		par->vSyncOn <<=1;		par->vSyncOff <<=1;	}	if (IS_VOODOO2(par)) {		/* voodoo2 has 32 pixel wide tiles , BUT stange things		   happen with odd number of tiles */		par->tiles_in_X = (info->var.xres + 63 ) / 64 * 2;	} else {		/* voodoo1 has 64 pixels wide tiles. */		par->tiles_in_X = (info->var.xres + 63 ) / 64;	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -