📄 intelfbhw.c
字号:
/* * intelfb * * Linux framebuffer driver for Intel(R) 865G integrated graphics chips. * * Copyright (C) 2002, 2003 David Dawes <dawes@tungstengraphics.com> * * 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.7 2003/02/06 00:53:11 dawes Exp $ *//* $TG$ */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/tty.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/fb.h>#include <linux/console.h>#include <linux/selection.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/vmalloc.h>#include <linux/kd.h>#include <linux/vt_kern.h>#include <linux/pagemap.h>#include <linux/version.h>#include <asm/io.h>#include <video/fbcon.h>#include <video/fbcon-cfb8.h>#include <video/fbcon-cfb16.h>#include <video/fbcon-cfb32.h>#include "intelfb.h"#include "intelfbhw.h"intintelfbhw_get_chipset(struct pci_dev *pdev, const char **name, int *chipset, int *mobile){ u32 tmp; if (!pdev || !name || !chipset || !mobile) return 1; switch (pdev->device) { case PCI_DEVICE_ID_INTEL_830M: *name = "Intel(R) 830M"; *chipset = INTEL_830M; *mobile = 1; return 0; case PCI_DEVICE_ID_INTEL_845G: *name = "Intel(R) 845G"; *chipset = INTEL_845G; *mobile = 0; return 0; case PCI_DEVICE_ID_INTEL_85XGM: tmp = 0; *mobile = 1; pci_read_config_dword(pdev, INTEL_85X_CAPID, &tmp); switch ((tmp >> INTEL_85X_VARIANT_SHIFT) & INTEL_85X_VARIANT_MASK) { case INTEL_VAR_855GME: *name = "Intel(R) 855GME"; *chipset = INTEL_855GME; return 0; case INTEL_VAR_855GM: *name = "Intel(R) 855GM"; *chipset = INTEL_855GM; return 0; case INTEL_VAR_852GME: *name = "Intel(R) 852GME"; *chipset = INTEL_852GME; return 0; case INTEL_VAR_852GM: *name = "Intel(R) 852GM"; *chipset = INTEL_852GM; return 0; default: *name = "Intel(R) 852GM/855GM"; *chipset = INTEL_85XGM; return 0; } break; case PCI_DEVICE_ID_INTEL_865G: *name = "Intel(R) 865G"; *chipset = INTEL_865G; *mobile = 0; return 0; default: return 1; }}intintelfbhw_get_memory(struct pci_dev *pdev, int *aperture_size, int *stolen_size){ struct pci_dev *bridge_dev; u16 tmp; if (!pdev || !aperture_size || !stolen_size) return 1; /* Find the bridge device. It is always 0:0.0 */ if (!(bridge_dev = pci_find_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); switch (pdev->device) { case PCI_DEVICE_ID_INTEL_830M: case PCI_DEVICE_ID_INTEL_845G: if ((tmp & INTEL_GMCH_MEM_MASK) == INTEL_GMCH_MEM_64M) *aperture_size = MB(64); else *aperture_size = MB(128); switch (tmp & INTEL_830_GMCH_GMS_MASK) { case INTEL_830_GMCH_GMS_STOLEN_512: *stolen_size = KB(512) - KB(132); return 0; case INTEL_830_GMCH_GMS_STOLEN_1024: *stolen_size = MB(1) - KB(132); return 0; case INTEL_830_GMCH_GMS_STOLEN_8192: *stolen_size = MB(8) - KB(132); 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: *aperture_size = MB(128); switch (tmp & INTEL_855_GMCH_GMS_MASK) { case INTEL_855_GMCH_GMS_STOLEN_1M: *stolen_size = MB(1) - KB(132); return 0; case INTEL_855_GMCH_GMS_STOLEN_4M: *stolen_size = MB(4) - KB(132); return 0; case INTEL_855_GMCH_GMS_STOLEN_8M: *stolen_size = MB(8) - KB(132); return 0; case INTEL_855_GMCH_GMS_STOLEN_16M: *stolen_size = MB(16) - KB(132); return 0; case INTEL_855_GMCH_GMS_STOLEN_32M: *stolen_size = MB(32) - KB(132); 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; } }}const char *intelfbhw_check_non_crt(struct intelfb_info *dinfo){ if (INREG(LVDS) & PORT_ENABLE) return "LVDS port"; else if (INREG(DVOA) & PORT_ENABLE) return "DVO port A"; else if (INREG(DVOB) & PORT_ENABLE) return "DVO port B"; else if (INREG(DVOC) & PORT_ENABLE) return "DVO port C"; else return NULL;}intintelfbhw_validate_mode(struct intelfb_info *dinfo, int con, struct fb_var_screeninfo *var){ int bytes_per_pixel; int tmp; DBG_MSG("intelfbhw_validate_mode\n"); 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->video_ram) { if (con >= 0) WRN_MSG("Not enough video ram for mode " "(%d KByte vs %d KByte).\n", BtoKB(tmp), BtoKB(dinfo->video_ram)); return 1; } /* Check if x/y limits are OK. */ if (var->xres - 1 > HACTIVE_MASK) { if (con >= 0) WRN_MSG("X resolution too large (%d vs %d).\n", var->xres, HACTIVE_MASK + 1); return 1; } if (var->yres - 1 > VACTIVE_MASK) { if (con >= 0) WRN_MSG("Y resolution too large (%d vs %d).\n", var->yres, VACTIVE_MASK + 1); return 1; } /* Check for interlaced/doublescan modes. */ if (var->vmode & FB_VMODE_INTERLACED) { if (con >= 0) WRN_MSG("Mode is interlaced.\n"); return 1; } if (var->vmode & FB_VMODE_DOUBLE) { if (con >= 0) WRN_MSG("Mode is double-scan.\n"); return 1; } /* Check if clock is OK. */ tmp = 1000000000 / var->pixclock; if (tmp < MIN_CLOCK) { if (con >= 0) 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) { if (con >= 0) WRN_MSG("Pixel clock is too high (%d MHz vs %d MHz).\n", (tmp + 500) / 1000, MAX_CLOCK / 1000); return 1; } return 0;}intintelfbhw_pan_display(struct fb_var_screeninfo *var, int con, struct fb_info *info){ struct intelfb_info *dinfo = GET_DINFO(info); u32 offset, xoffset, yoffset; DBG_MSG("intelfbhw_pan_display\n"); if (con != dinfo->currcon) return 0; 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; OUTREG(DSPABASE, offset); return 0;}/* Blank the screen. */voidintelfbhw_do_blank(int blank, struct fb_info *info){ struct intelfb_info *dinfo = GET_DINFO(info); u32 tmp; DBG_MSG("intelfbhw_do_blank: blank is %d\n", blank); /* 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.enabled is %d\n", dinfo->cursor.enabled);#endif if (dinfo->cursor.enabled) { if (blank) { intelfbhw_cursor_hide(dinfo); } else { intelfbhw_cursor_show(dinfo); } dinfo->cursor.enabled = 1; } dinfo->cursor.blanked = blank; /* Set DPMS level */ tmp = INREG(ADPA) & ~ADPA_DPMS_CONTROL_MASK; switch (blank) { case 0: case 1: tmp |= ADPA_DPMS_D0; break; case 2: tmp |= ADPA_DPMS_D1; break; case 3: tmp |= ADPA_DPMS_D2; break; case 4: tmp |= ADPA_DPMS_D3; break; } OUTREG(ADPA, tmp); return;}voidintelfbhw_setcolreg(struct intelfb_info *dinfo, unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp){#if VERBOSE > 1 DBG_MSG("intelfbhw_setcolreg: %d: (%d, %d, %d)\n", regno, red, green, blue);#endif u32 palette_reg = (dinfo->pipe == PIPE_A) ? PALETTE_A : PALETTE_B; OUTREG(palette_reg + (regno << 2), (red << PALETTE_8_RED_SHIFT) | (green << PALETTE_8_GREEN_SHIFT) | (blue << PALETTE_8_BLUE_SHIFT));}intintelfbhw_read_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw, int flag){ int i; DBG_MSG("intelfbhw_read_hw_state\n"); 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); hw->vsync_a = INREG(VSYNC_A); hw->src_size_a = INREG(SRC_SIZE_A); hw->bclrpat_a = INREG(BCLRPAT_A); hw->htotal_b = INREG(HTOTAL_B); hw->hblank_b = INREG(HBLANK_B); hw->hsync_b = INREG(HSYNC_B); hw->vtotal_b = INREG(VTOTAL_B); hw->vblank_b = INREG(VBLANK_B); hw->vsync_b = INREG(VSYNC_B); hw->src_size_b = INREG(SRC_SIZE_B); hw->bclrpat_b = INREG(BCLRPAT_B); if (flag == 3) return flag; hw->adpa = INREG(ADPA); hw->dvoa = INREG(DVOA); hw->dvob = INREG(DVOB); hw->dvoc = INREG(DVOC); hw->dvoa_srcdim = INREG(DVOA_SRCDIM); hw->dvob_srcdim = INREG(DVOB_SRCDIM); hw->dvoc_srcdim = INREG(DVOC_SRCDIM); hw->lvds = INREG(LVDS); if (flag == 4) return flag; hw->pipe_a_conf = INREG(PIPEACONF); hw->pipe_b_conf = INREG(PIPEBCONF); hw->disp_arb = INREG(DISPARB); if (flag == 5) return flag; hw->cursor_a_control = INREG(CURSOR_A_CONTROL); hw->cursor_b_control = INREG(CURSOR_B_CONTROL); hw->cursor_a_base = INREG(CURSOR_A_BASEADDR); hw->cursor_b_base = INREG(CURSOR_B_BASEADDR); if (flag == 6) return flag; for (i = 0; i < 4; i++) { hw->cursor_a_palette[i] = INREG(CURSOR_A_PALETTE0 + (i << 2)); hw->cursor_b_palette[i] = INREG(CURSOR_B_PALETTE0 + (i << 2)); } if (flag == 7) return flag; hw->cursor_size = INREG(CURSOR_SIZE); if (flag == 8) return flag; hw->disp_a_ctrl = INREG(DSPACNTR); hw->disp_b_ctrl = INREG(DSPBCNTR); hw->disp_a_base = INREG(DSPABASE); hw->disp_b_base = INREG(DSPBBASE); hw->disp_a_stride = INREG(DSPASTRIDE); hw->disp_b_stride = INREG(DSPBSTRIDE); if (flag == 9) return flag; hw->vgacntrl = INREG(VGACNTRL); if (flag == 10) return flag; hw->add_id = INREG(ADD_ID); if (flag == 11) return flag; for (i = 0; i < 7; i++) { hw->swf0x[i] = INREG(SWF00 + (i << 2)); hw->swf1x[i] = INREG(SWF10 + (i << 2)); if (i < 3) hw->swf3x[i] = INREG(SWF30 + (i << 2)); } for (i = 0; i < 8; i++) hw->fence[i] = INREG(FENCE + (i << 2)); hw->instpm = INREG(INSTPM); hw->mem_mode = INREG(MEM_MODE); hw->fw_blc_0 = INREG(FW_BLC_0); hw->fw_blc_1 = INREG(FW_BLC_1); return 0;}voidintelfbhw_print_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw){#if REGDUMP int i, m1, m2, n, p1, p2; DBG_MSG("intelfbhw_print_hw_state\n"); if (!hw || !dinfo) return; /* Read in as much of the HW state as possible. */ printk("hw state dump start\n"); printk(" VGA0_DIVISOR: 0x%08x\n", hw->vga0_divisor); printk(" VGA1_DIVISOR: 0x%08x\n", hw->vga1_divisor); printk(" VGAPD: 0x%08x\n", hw->vga_pd); n = (hw->vga0_divisor >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK; m1 = (hw->vga0_divisor >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK; m2 = (hw->vga0_divisor >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK; if (hw->vga_pd & VGAPD_0_P1_FORCE_DIV2) p1 = 0; else p1 = (hw->vga_pd >> VGAPD_0_P1_SHIFT) & DPLL_P1_MASK; p2 = (hw->vga_pd >> VGAPD_0_P2_SHIFT) & DPLL_P2_MASK; printk(" VGA0: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n", m1, m2, n, p1, p2); printk(" VGA0: clock is %d\n", CALC_VCLOCK(m1, m2, n, p1, p2)); n = (hw->vga1_divisor >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK; m1 = (hw->vga1_divisor >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK; m2 = (hw->vga1_divisor >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK; if (hw->vga_pd & VGAPD_1_P1_FORCE_DIV2) p1 = 0; else p1 = (hw->vga_pd >> VGAPD_1_P1_SHIFT) & DPLL_P1_MASK; p2 = (hw->vga_pd >> VGAPD_1_P2_SHIFT) & DPLL_P2_MASK; printk(" VGA1: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n", m1, m2, n, p1, p2); printk(" VGA1: clock is %d\n", CALC_VCLOCK(m1, m2, n, p1, p2)); printk(" DPLL_A: 0x%08x\n", hw->dpll_a); printk(" DPLL_B: 0x%08x\n", hw->dpll_b); printk(" FPA0: 0x%08x\n", hw->fpa0); printk(" FPA1: 0x%08x\n", hw->fpa1); printk(" FPB0: 0x%08x\n", hw->fpb0); printk(" FPB1: 0x%08x\n", hw->fpb1); n = (hw->fpa0 >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK; m1 = (hw->fpa0 >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK; m2 = (hw->fpa0 >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK; if (hw->dpll_a & DPLL_P1_FORCE_DIV2) p1 = 0; else p1 = (hw->dpll_a >> DPLL_P1_SHIFT) & DPLL_P1_MASK; p2 = (hw->dpll_a >> DPLL_P2_SHIFT) & DPLL_P2_MASK; printk(" PLLA0: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n", m1, m2, n, p1, p2); printk(" PLLA0: clock is %d\n", CALC_VCLOCK(m1, m2, n, p1, p2)); n = (hw->fpa1 >> FP_N_DIVISOR_SHIFT) & FP_DIVISOR_MASK; m1 = (hw->fpa1 >> FP_M1_DIVISOR_SHIFT) & FP_DIVISOR_MASK; m2 = (hw->fpa1 >> FP_M2_DIVISOR_SHIFT) & FP_DIVISOR_MASK; if (hw->dpll_a & DPLL_P1_FORCE_DIV2) p1 = 0; else p1 = (hw->dpll_a >> DPLL_P1_SHIFT) & DPLL_P1_MASK; p2 = (hw->dpll_a >> DPLL_P2_SHIFT) & DPLL_P2_MASK; printk(" PLLA1: (m1, m2, n, p1, p2) = (%d, %d, %d, %d, %d)\n", m1, m2, n, p1, p2); printk(" PLLA1: clock is %d\n", CALC_VCLOCK(m1, m2, n, p1, p2)); #if 0 printk(" PALETTE_A:\n"); for (i = 0; i < PALETTE_8_ENTRIES) printk(" %3d: 0x%08x\n", i, hw->palette_a[i]; printk(" PALETTE_B:\n"); for (i = 0; i < PALETTE_8_ENTRIES) printk(" %3d: 0x%08x\n", i, hw->palette_b[i];#endif printk(" HTOTAL_A: 0x%08x\n", hw->htotal_a); printk(" HBLANK_A: 0x%08x\n", hw->hblank_a); printk(" HSYNC_A: 0x%08x\n", hw->hsync_a); printk(" VTOTAL_A: 0x%08x\n", hw->vtotal_a); printk(" VBLANK_A: 0x%08x\n", hw->vblank_a); printk(" VSYNC_A: 0x%08x\n", hw->vsync_a);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -