📄 intelfbhw.c
字号:
/* * 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 + -