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

📄 intelfbhw.c

📁 Linux环境下视频显示卡设备的驱动程序源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * intelfb * * Linux framebuffer driver for Intel(R) 865G integrated graphics chips. * * Copyright © 2002, 2003 David Dawes <dawes@xfree86.org> *                   2004 Sylvain Meyer * * This driver consists of two parts.  The first part (intelfbdrv.c) provides * the basic fbdev interfaces, is derived in part from the radeonfb and * vesafb drivers, and is covered by the GPL.  The second part (intelfbhw.c) * provides the code to program the hardware.  Most of it is derived from * the i810/i830 XFree86 driver.  The HW-specific code is covered here * under a dual license (GPL and MIT/XFree86 license). * * Author: David Dawes * *//* $DHD: intelfb/intelfbhw.c,v 1.9 2003/06/27 15:06:25 dawes Exp $ */#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/fb.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/vmalloc.h>#include <linux/pagemap.h>#include <linux/interrupt.h>#include <asm/io.h>#include "intelfb.h"#include "intelfbhw.h"struct pll_min_max {	int min_m, max_m, min_m1, max_m1;	int min_m2, max_m2, min_n, max_n;	int min_p, max_p, min_p1, max_p1;	int min_vco, max_vco, p_transition_clk, ref_clk;	int p_inc_lo, p_inc_hi;};#define PLLS_I8xx 0#define PLLS_I9xx 1#define PLLS_MAX 2static struct pll_min_max plls[PLLS_MAX] = {	{ 108, 140, 18, 26,	  6, 16, 3, 16,	  4, 128, 0, 31,	  930000, 1400000, 165000, 48000,	  4, 2 },		/* I8xx */	{ 75, 120, 10, 20,	  5, 9, 4, 7,	  5, 80, 1, 8,	  1400000, 2800000, 200000, 96000,	  10, 5 }		/* I9xx */};int intelfbhw_get_chipset(struct pci_dev *pdev, struct intelfb_info *dinfo){	u32 tmp;	if (!pdev || !dinfo)		return 1;	switch (pdev->device) {	case PCI_DEVICE_ID_INTEL_830M:		dinfo->name = "Intel(R) 830M";		dinfo->chipset = INTEL_830M;		dinfo->mobile = 1;		dinfo->pll_index = PLLS_I8xx;		return 0;	case PCI_DEVICE_ID_INTEL_845G:		dinfo->name = "Intel(R) 845G";		dinfo->chipset = INTEL_845G;		dinfo->mobile = 0;		dinfo->pll_index = PLLS_I8xx;		return 0;	case PCI_DEVICE_ID_INTEL_85XGM:		tmp = 0;		dinfo->mobile = 1;		dinfo->pll_index = PLLS_I8xx;		pci_read_config_dword(pdev, INTEL_85X_CAPID, &tmp);		switch ((tmp >> INTEL_85X_VARIANT_SHIFT) &			INTEL_85X_VARIANT_MASK) {		case INTEL_VAR_855GME:			dinfo->name = "Intel(R) 855GME";			dinfo->chipset = INTEL_855GME;			return 0;		case INTEL_VAR_855GM:			dinfo->name = "Intel(R) 855GM";			dinfo->chipset = INTEL_855GM;			return 0;		case INTEL_VAR_852GME:			dinfo->name = "Intel(R) 852GME";			dinfo->chipset = INTEL_852GME;			return 0;		case INTEL_VAR_852GM:			dinfo->name = "Intel(R) 852GM";			dinfo->chipset = INTEL_852GM;			return 0;		default:			dinfo->name = "Intel(R) 852GM/855GM";			dinfo->chipset = INTEL_85XGM;			return 0;		}		break;	case PCI_DEVICE_ID_INTEL_865G:		dinfo->name = "Intel(R) 865G";		dinfo->chipset = INTEL_865G;		dinfo->mobile = 0;		dinfo->pll_index = PLLS_I8xx;		return 0;	case PCI_DEVICE_ID_INTEL_915G:		dinfo->name = "Intel(R) 915G";		dinfo->chipset = INTEL_915G;		dinfo->mobile = 0;		dinfo->pll_index = PLLS_I9xx;		return 0;	case PCI_DEVICE_ID_INTEL_915GM:		dinfo->name = "Intel(R) 915GM";		dinfo->chipset = INTEL_915GM;		dinfo->mobile = 1;		dinfo->pll_index = PLLS_I9xx;		return 0;	case PCI_DEVICE_ID_INTEL_945G:		dinfo->name = "Intel(R) 945G";		dinfo->chipset = INTEL_945G;		dinfo->mobile = 0;		dinfo->pll_index = PLLS_I9xx;		return 0;	case PCI_DEVICE_ID_INTEL_945GM:		dinfo->name = "Intel(R) 945GM";		dinfo->chipset = INTEL_945GM;		dinfo->mobile = 1;		dinfo->pll_index = PLLS_I9xx;		return 0;	case PCI_DEVICE_ID_INTEL_945GME:		dinfo->name = "Intel(R) 945GME";		dinfo->chipset = INTEL_945GME;		dinfo->mobile = 1;		dinfo->pll_index = PLLS_I9xx;		return 0;	case PCI_DEVICE_ID_INTEL_965G:		dinfo->name = "Intel(R) 965G";		dinfo->chipset = INTEL_965G;		dinfo->mobile = 0;		dinfo->pll_index = PLLS_I9xx;		return 0;	case PCI_DEVICE_ID_INTEL_965GM:		dinfo->name = "Intel(R) 965GM";		dinfo->chipset = INTEL_965GM;		dinfo->mobile = 1;		dinfo->pll_index = PLLS_I9xx;		return 0;	default:		return 1;	}}int intelfbhw_get_memory(struct pci_dev *pdev, int *aperture_size,			 int *stolen_size){	struct pci_dev *bridge_dev;	u16 tmp;	int stolen_overhead;	if (!pdev || !aperture_size || !stolen_size)		return 1;	/* Find the bridge device.  It is always 0:0.0 */	if (!(bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0)))) {		ERR_MSG("cannot find bridge device\n");		return 1;	}	/* Get the fb aperture size and "stolen" memory amount. */	tmp = 0;	pci_read_config_word(bridge_dev, INTEL_GMCH_CTRL, &tmp);	pci_dev_put(bridge_dev);	switch (pdev->device) {	case PCI_DEVICE_ID_INTEL_915G:	case PCI_DEVICE_ID_INTEL_915GM:	case PCI_DEVICE_ID_INTEL_945G:	case PCI_DEVICE_ID_INTEL_945GM:	case PCI_DEVICE_ID_INTEL_945GME:	case PCI_DEVICE_ID_INTEL_965G:	case PCI_DEVICE_ID_INTEL_965GM:		/* 915, 945 and 965 chipsets support a 256MB aperture.		   Aperture size is determined by inspected the		   base address of the aperture. */		if (pci_resource_start(pdev, 2) & 0x08000000)			*aperture_size = MB(128);		else			*aperture_size = MB(256);		break;	default:		if ((tmp & INTEL_GMCH_MEM_MASK) == INTEL_GMCH_MEM_64M)			*aperture_size = MB(64);		else			*aperture_size = MB(128);		break;	}	/* Stolen memory size is reduced by the GTT and the popup.	   GTT is 1K per MB of aperture size, and popup is 4K. */	stolen_overhead = (*aperture_size / MB(1)) + 4;	switch(pdev->device) {	case PCI_DEVICE_ID_INTEL_830M:	case PCI_DEVICE_ID_INTEL_845G:		switch (tmp & INTEL_830_GMCH_GMS_MASK) {		case INTEL_830_GMCH_GMS_STOLEN_512:			*stolen_size = KB(512) - KB(stolen_overhead);			return 0;		case INTEL_830_GMCH_GMS_STOLEN_1024:			*stolen_size = MB(1) - KB(stolen_overhead);			return 0;		case INTEL_830_GMCH_GMS_STOLEN_8192:			*stolen_size = MB(8) - KB(stolen_overhead);			return 0;		case INTEL_830_GMCH_GMS_LOCAL:			ERR_MSG("only local memory found\n");			return 1;		case INTEL_830_GMCH_GMS_DISABLED:			ERR_MSG("video memory is disabled\n");			return 1;		default:			ERR_MSG("unexpected GMCH_GMS value: 0x%02x\n",				tmp & INTEL_830_GMCH_GMS_MASK);			return 1;		}		break;	default:		switch (tmp & INTEL_855_GMCH_GMS_MASK) {		case INTEL_855_GMCH_GMS_STOLEN_1M:			*stolen_size = MB(1) - KB(stolen_overhead);			return 0;		case INTEL_855_GMCH_GMS_STOLEN_4M:			*stolen_size = MB(4) - KB(stolen_overhead);			return 0;		case INTEL_855_GMCH_GMS_STOLEN_8M:			*stolen_size = MB(8) - KB(stolen_overhead);			return 0;		case INTEL_855_GMCH_GMS_STOLEN_16M:			*stolen_size = MB(16) - KB(stolen_overhead);			return 0;		case INTEL_855_GMCH_GMS_STOLEN_32M:			*stolen_size = MB(32) - KB(stolen_overhead);			return 0;		case INTEL_915G_GMCH_GMS_STOLEN_48M:			*stolen_size = MB(48) - KB(stolen_overhead);			return 0;		case INTEL_915G_GMCH_GMS_STOLEN_64M:			*stolen_size = MB(64) - KB(stolen_overhead);			return 0;		case INTEL_855_GMCH_GMS_DISABLED:			ERR_MSG("video memory is disabled\n");			return 0;		default:			ERR_MSG("unexpected GMCH_GMS value: 0x%02x\n",				tmp & INTEL_855_GMCH_GMS_MASK);			return 1;		}	}}int intelfbhw_check_non_crt(struct intelfb_info *dinfo){	int dvo = 0;	if (INREG(LVDS) & PORT_ENABLE)		dvo |= LVDS_PORT;	if (INREG(DVOA) & PORT_ENABLE)		dvo |= DVOA_PORT;	if (INREG(DVOB) & PORT_ENABLE)		dvo |= DVOB_PORT;	if (INREG(DVOC) & PORT_ENABLE)		dvo |= DVOC_PORT;	return dvo;}const char * intelfbhw_dvo_to_string(int dvo){	if (dvo & DVOA_PORT)		return "DVO port A";	else if (dvo & DVOB_PORT)		return "DVO port B";	else if (dvo & DVOC_PORT)		return "DVO port C";	else if (dvo & LVDS_PORT)		return "LVDS port";	else		return NULL;}int intelfbhw_validate_mode(struct intelfb_info *dinfo,			    struct fb_var_screeninfo *var){	int bytes_per_pixel;	int tmp;#if VERBOSE > 0	DBG_MSG("intelfbhw_validate_mode\n");#endif	bytes_per_pixel = var->bits_per_pixel / 8;	if (bytes_per_pixel == 3)		bytes_per_pixel = 4;	/* Check if enough video memory. */	tmp = var->yres_virtual * var->xres_virtual * bytes_per_pixel;	if (tmp > dinfo->fb.size) {		WRN_MSG("Not enough video ram for mode "			"(%d KByte vs %d KByte).\n",			BtoKB(tmp), BtoKB(dinfo->fb.size));		return 1;	}	/* Check if x/y limits are OK. */	if (var->xres - 1 > HACTIVE_MASK) {		WRN_MSG("X resolution too large (%d vs %d).\n",			var->xres, HACTIVE_MASK + 1);		return 1;	}	if (var->yres - 1 > VACTIVE_MASK) {		WRN_MSG("Y resolution too large (%d vs %d).\n",			var->yres, VACTIVE_MASK + 1);		return 1;	}	if (var->xres < 4) {		WRN_MSG("X resolution too small (%d vs 4).\n", var->xres);		return 1;	}	if (var->yres < 4) {		WRN_MSG("Y resolution too small (%d vs 4).\n", var->yres);		return 1;	}	/* Check for doublescan modes. */	if (var->vmode & FB_VMODE_DOUBLE) {		WRN_MSG("Mode is double-scan.\n");		return 1;	}	if ((var->vmode & FB_VMODE_INTERLACED) && (var->yres & 1)) {		WRN_MSG("Odd number of lines in interlaced mode\n");		return 1;	}	/* Check if clock is OK. */	tmp = 1000000000 / var->pixclock;	if (tmp < MIN_CLOCK) {		WRN_MSG("Pixel clock is too low (%d MHz vs %d MHz).\n",			(tmp + 500) / 1000, MIN_CLOCK / 1000);		return 1;	}	if (tmp > MAX_CLOCK) {		WRN_MSG("Pixel clock is too high (%d MHz vs %d MHz).\n",			(tmp + 500) / 1000, MAX_CLOCK / 1000);		return 1;	}	return 0;}int intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info){	struct intelfb_info *dinfo = GET_DINFO(info);	u32 offset, xoffset, yoffset;#if VERBOSE > 0	DBG_MSG("intelfbhw_pan_display\n");#endif	xoffset = ROUND_DOWN_TO(var->xoffset, 8);	yoffset = var->yoffset;	if ((xoffset + var->xres > var->xres_virtual) ||	    (yoffset + var->yres > var->yres_virtual))		return -EINVAL;	offset = (yoffset * dinfo->pitch) +		 (xoffset * var->bits_per_pixel) / 8;	offset += dinfo->fb.offset << 12;	dinfo->vsync.pan_offset = offset;	if ((var->activate & FB_ACTIVATE_VBL) &&	    !intelfbhw_enable_irq(dinfo))		dinfo->vsync.pan_display = 1;	else {		dinfo->vsync.pan_display = 0;		OUTREG(DSPABASE, offset);	}	return 0;}/* Blank the screen. */void intelfbhw_do_blank(int blank, struct fb_info *info){	struct intelfb_info *dinfo = GET_DINFO(info);	u32 tmp;#if VERBOSE > 0	DBG_MSG("intelfbhw_do_blank: blank is %d\n", blank);#endif	/* Turn plane A on or off */	tmp = INREG(DSPACNTR);	if (blank)		tmp &= ~DISPPLANE_PLANE_ENABLE;	else		tmp |= DISPPLANE_PLANE_ENABLE;	OUTREG(DSPACNTR, tmp);	/* Flush */	tmp = INREG(DSPABASE);	OUTREG(DSPABASE, tmp);	/* Turn off/on the HW cursor */#if VERBOSE > 0	DBG_MSG("cursor_on is %d\n", dinfo->cursor_on);#endif	if (dinfo->cursor_on) {		if (blank)			intelfbhw_cursor_hide(dinfo);		else			intelfbhw_cursor_show(dinfo);		dinfo->cursor_on = 1;	}	dinfo->cursor_blanked = blank;	/* Set DPMS level */	tmp = INREG(ADPA) & ~ADPA_DPMS_CONTROL_MASK;	switch (blank) {	case FB_BLANK_UNBLANK:	case FB_BLANK_NORMAL:		tmp |= ADPA_DPMS_D0;		break;	case FB_BLANK_VSYNC_SUSPEND:		tmp |= ADPA_DPMS_D1;		break;	case FB_BLANK_HSYNC_SUSPEND:		tmp |= ADPA_DPMS_D2;		break;	case FB_BLANK_POWERDOWN:		tmp |= ADPA_DPMS_D3;		break;	}	OUTREG(ADPA, tmp);	return;}void intelfbhw_setcolreg(struct intelfb_info *dinfo, unsigned regno,			 unsigned red, unsigned green, unsigned blue,			 unsigned transp){	u32 palette_reg = (dinfo->pipe == PIPE_A) ?			  PALETTE_A : PALETTE_B;#if VERBOSE > 0	DBG_MSG("intelfbhw_setcolreg: %d: (%d, %d, %d)\n",		regno, red, green, blue);#endif	OUTREG(palette_reg + (regno << 2),	       (red << PALETTE_8_RED_SHIFT) |	       (green << PALETTE_8_GREEN_SHIFT) |	       (blue << PALETTE_8_BLUE_SHIFT));}int intelfbhw_read_hw_state(struct intelfb_info *dinfo,			    struct intelfb_hwstate *hw, int flag){	int i;#if VERBOSE > 0	DBG_MSG("intelfbhw_read_hw_state\n");#endif	if (!hw || !dinfo)		return -1;	/* Read in as much of the HW state as possible. */	hw->vga0_divisor = INREG(VGA0_DIVISOR);	hw->vga1_divisor = INREG(VGA1_DIVISOR);	hw->vga_pd = INREG(VGAPD);	hw->dpll_a = INREG(DPLL_A);	hw->dpll_b = INREG(DPLL_B);	hw->fpa0 = INREG(FPA0);	hw->fpa1 = INREG(FPA1);	hw->fpb0 = INREG(FPB0);	hw->fpb1 = INREG(FPB1);	if (flag == 1)		return flag;#if 0	/* This seems to be a problem with the 852GM/855GM */	for (i = 0; i < PALETTE_8_ENTRIES; i++) {		hw->palette_a[i] = INREG(PALETTE_A + (i << 2));		hw->palette_b[i] = INREG(PALETTE_B + (i << 2));	}#endif	if (flag == 2)		return flag;	hw->htotal_a = INREG(HTOTAL_A);	hw->hblank_a = INREG(HBLANK_A);	hw->hsync_a = INREG(HSYNC_A);	hw->vtotal_a = INREG(VTOTAL_A);	hw->vblank_a = INREG(VBLANK_A);

⌨️ 快捷键说明

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