X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;ds=sidebyside;f=drivers%2Fvideo%2Fintelfb%2Fintelfbhw.c;h=38065cf94ac4cf5dbaf195233871712af4607ab6;hb=49c39b4953e545ce3b5957cce22e1ade01c6e642;hp=a3a94642b79bdba54713e1c3496829e6729b1b5f;hpb=7258b11d2e9a47d2b01620622579f22906960e1a;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c index a3a9464..38065cf 100644 --- a/drivers/video/intelfb/intelfbhw.c +++ b/drivers/video/intelfb/intelfbhw.c @@ -3,7 +3,7 @@ * * Linux framebuffer driver for Intel(R) 865G integrated graphics chips. * - * Copyright © 2002, 2003 David Dawes + * Copyright © 2002, 2003 David Dawes * 2004 Sylvain Meyer * * This driver consists of two parts. The first part (intelfbdrv.c) provides @@ -19,14 +19,11 @@ /* $DHD: intelfb/intelfbhw.c,v 1.9 2003/06/27 15:06:25 dawes Exp $ */ -#include #include #include #include #include #include -#include -#include #include #include #include @@ -34,6 +31,7 @@ #include #include #include +#include #include @@ -41,105 +39,149 @@ #include "intelfbhw.h" struct pll_min_max { - int min_m, max_m; - int min_m1, max_m1; - int min_m2, max_m2; - int min_n, max_n; - int min_p, max_p; - int min_p1, max_p1; - int min_vco_freq, max_vco_freq; - int p_transition_clock; + 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 2 -struct pll_min_max plls[PLLS_MAX] = { - { 108, 140, 18, 26, 6, 16, 3, 16, 4, 128, 0, 31, 930000, 1400000, 165000 }, //I8xx - { 75, 120, 10, 20, 5, 9, 4, 7, 5, 80, 1, 8, 930000, 2800000, 200000 } //I9xx +static 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, const char **name, int *chipset, - int *mobile) +int intelfbhw_get_chipset(struct pci_dev *pdev, struct intelfb_info *dinfo) { u32 tmp; - - if (!pdev || !name || !chipset || !mobile) + if (!pdev || !dinfo) return 1; switch (pdev->device) { case PCI_DEVICE_ID_INTEL_830M: - *name = "Intel(R) 830M"; - *chipset = INTEL_830M; - *mobile = 1; + 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: - *name = "Intel(R) 845G"; - *chipset = INTEL_845G; - *mobile = 0; + dinfo->name = "Intel(R) 845G"; + dinfo->chipset = INTEL_845G; + dinfo->mobile = 0; + dinfo->pll_index = PLLS_I8xx; + return 0; + case PCI_DEVICE_ID_INTEL_854: + dinfo->mobile = 1; + dinfo->name = "Intel(R) 854"; + dinfo->chipset = INTEL_854; return 0; case PCI_DEVICE_ID_INTEL_85XGM: tmp = 0; - *mobile = 1; + 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: - *name = "Intel(R) 855GME"; - *chipset = INTEL_855GME; + dinfo->name = "Intel(R) 855GME"; + dinfo->chipset = INTEL_855GME; return 0; case INTEL_VAR_855GM: - *name = "Intel(R) 855GM"; - *chipset = INTEL_855GM; + dinfo->name = "Intel(R) 855GM"; + dinfo->chipset = INTEL_855GM; return 0; case INTEL_VAR_852GME: - *name = "Intel(R) 852GME"; - *chipset = INTEL_852GME; + dinfo->name = "Intel(R) 852GME"; + dinfo->chipset = INTEL_852GME; return 0; case INTEL_VAR_852GM: - *name = "Intel(R) 852GM"; - *chipset = INTEL_852GM; + dinfo->name = "Intel(R) 852GM"; + dinfo->chipset = INTEL_852GM; return 0; default: - *name = "Intel(R) 852GM/855GM"; - *chipset = INTEL_85XGM; + dinfo->name = "Intel(R) 852GM/855GM"; + dinfo->chipset = INTEL_85XGM; return 0; } break; case PCI_DEVICE_ID_INTEL_865G: - *name = "Intel(R) 865G"; - *chipset = INTEL_865G; - *mobile = 0; + 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: - *name = "Intel(R) 915G"; - *chipset = INTEL_915G; - *mobile = 0; + 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: - *name = "Intel(R) 915GM"; - *chipset = INTEL_915GM; - *mobile = 1; + 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) +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_find_slot(0, PCI_DEVFN(0, 0)))) { + if (!(bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0)))) { ERR_MSG("cannot find bridge device\n"); return 1; } @@ -147,22 +189,47 @@ intelfbhw_get_memory(struct pci_dev *pdev, int *aperture_size, /* 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_830M: - case PCI_DEVICE_ID_INTEL_845G: + 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(132); + *stolen_size = KB(512) - KB(stolen_overhead); return 0; case INTEL_830_GMCH_GMS_STOLEN_1024: - *stolen_size = MB(1) - KB(132); + *stolen_size = MB(1) - KB(stolen_overhead); return 0; case INTEL_830_GMCH_GMS_STOLEN_8192: - *stolen_size = MB(8) - KB(132); + *stolen_size = MB(8) - KB(stolen_overhead); return 0; case INTEL_830_GMCH_GMS_LOCAL: ERR_MSG("only local memory found\n"); @@ -177,28 +244,27 @@ intelfbhw_get_memory(struct pci_dev *pdev, int *aperture_size, } 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); + *stolen_size = MB(1) - KB(stolen_overhead); return 0; case INTEL_855_GMCH_GMS_STOLEN_4M: - *stolen_size = MB(4) - KB(132); + *stolen_size = MB(4) - KB(stolen_overhead); return 0; case INTEL_855_GMCH_GMS_STOLEN_8M: - *stolen_size = MB(8) - KB(132); + *stolen_size = MB(8) - KB(stolen_overhead); return 0; case INTEL_855_GMCH_GMS_STOLEN_16M: - *stolen_size = MB(16) - KB(132); + *stolen_size = MB(16) - KB(stolen_overhead); return 0; case INTEL_855_GMCH_GMS_STOLEN_32M: - *stolen_size = MB(32) - KB(132); + *stolen_size = MB(32) - KB(stolen_overhead); return 0; case INTEL_915G_GMCH_GMS_STOLEN_48M: - *stolen_size = MB(48) - KB(132); + *stolen_size = MB(48) - KB(stolen_overhead); return 0; case INTEL_915G_GMCH_GMS_STOLEN_64M: - *stolen_size = MB(64) - KB(132); + *stolen_size = MB(64) - KB(stolen_overhead); return 0; case INTEL_855_GMCH_GMS_DISABLED: ERR_MSG("video memory is disabled\n"); @@ -211,8 +277,7 @@ intelfbhw_get_memory(struct pci_dev *pdev, int *aperture_size, } } -int -intelfbhw_check_non_crt(struct intelfb_info *dinfo) +int intelfbhw_check_non_crt(struct intelfb_info *dinfo) { int dvo = 0; @@ -228,8 +293,7 @@ intelfbhw_check_non_crt(struct intelfb_info *dinfo) return dvo; } -const char * -intelfbhw_dvo_to_string(int dvo) +const char * intelfbhw_dvo_to_string(int dvo) { if (dvo & DVOA_PORT) return "DVO port A"; @@ -244,9 +308,8 @@ intelfbhw_dvo_to_string(int dvo) } -int -intelfbhw_validate_mode(struct intelfb_info *dinfo, - struct fb_var_screeninfo *var) +int intelfbhw_validate_mode(struct intelfb_info *dinfo, + struct fb_var_screeninfo *var) { int bytes_per_pixel; int tmp; @@ -279,17 +342,26 @@ intelfbhw_validate_mode(struct intelfb_info *dinfo, var->yres, VACTIVE_MASK + 1); return 1; } - - /* Check for interlaced/doublescan modes. */ - if (var->vmode & FB_VMODE_INTERLACED) { - WRN_MSG("Mode is interlaced.\n"); + 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) { @@ -306,8 +378,7 @@ intelfbhw_validate_mode(struct intelfb_info *dinfo, return 0; } -int -intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +int intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { struct intelfb_info *dinfo = GET_DINFO(info); u32 offset, xoffset, yoffset; @@ -328,14 +399,20 @@ intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) offset += dinfo->fb.offset << 12; - OUTREG(DSPABASE, offset); + 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) +void intelfbhw_do_blank(int blank, struct fb_info *info) { struct intelfb_info *dinfo = GET_DINFO(info); u32 tmp; @@ -360,11 +437,10 @@ intelfbhw_do_blank(int blank, struct fb_info *info) DBG_MSG("cursor_on is %d\n", dinfo->cursor_on); #endif if (dinfo->cursor_on) { - if (blank) { + if (blank) intelfbhw_cursor_hide(dinfo); - } else { + else intelfbhw_cursor_show(dinfo); - } dinfo->cursor_on = 1; } dinfo->cursor_blanked = blank; @@ -392,19 +468,44 @@ intelfbhw_do_blank(int blank, struct fb_info *info) } -void -intelfbhw_setcolreg(struct intelfb_info *dinfo, unsigned regno, - unsigned red, unsigned green, unsigned blue, - unsigned transp) +/* Check which pipe is connected to an active display plane. */ +int intelfbhw_active_pipe(const struct intelfb_hwstate *hw) { + int pipe = -1; + + /* keep old default behaviour - prefer PIPE_A */ + if (hw->disp_b_ctrl & DISPPLANE_PLANE_ENABLE) { + pipe = (hw->disp_b_ctrl >> DISPPLANE_SEL_PIPE_SHIFT); + pipe &= PIPE_MASK; + if (unlikely(pipe == PIPE_A)) + return PIPE_A; + } + if (hw->disp_a_ctrl & DISPPLANE_PLANE_ENABLE) { + pipe = (hw->disp_a_ctrl >> DISPPLANE_SEL_PIPE_SHIFT); + pipe &= PIPE_MASK; + if (likely(pipe == PIPE_A)) + return PIPE_A; + } + /* Impossible that no pipe is selected - return PIPE_A */ + WARN_ON(pipe == -1); + if (unlikely(pipe == -1)) + pipe = PIPE_A; + + return pipe; +} + +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 - 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) | @@ -412,9 +513,8 @@ intelfbhw_setcolreg(struct intelfb_info *dinfo, unsigned regno, } -int -intelfbhw_read_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw, - int flag) +int intelfbhw_read_hw_state(struct intelfb_info *dinfo, + struct intelfb_hwstate *hw, int flag) { int i; @@ -545,48 +645,103 @@ intelfbhw_read_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw, hw->fw_blc_0 = INREG(FW_BLC_0); hw->fw_blc_1 = INREG(FW_BLC_1); + hw->hwstam = INREG16(HWSTAM); + hw->ier = INREG16(IER); + hw->iir = INREG16(IIR); + hw->imr = INREG16(IMR); + return 0; } -void -intelfbhw_print_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw) +static int calc_vclock3(int index, int m, int n, int p) +{ + if (p == 0 || n == 0) + return 0; + return plls[index].ref_clk * m / n / p; +} + +static int calc_vclock(int index, int m1, int m2, int n, int p1, int p2, + int lvds) { + struct pll_min_max *pll = &plls[index]; + u32 m, vco, p; + + m = (5 * (m1 + 2)) + (m2 + 2); + n += 2; + vco = pll->ref_clk * m / n; + + if (index == PLLS_I8xx) + p = ((p1 + 2) * (1 << (p2 + 1))); + else + p = ((p1) * (p2 ? 5 : 10)); + return vco / p; +} + #if REGDUMP - int i, m1, m2, n, p1, p2; +static void intelfbhw_get_p1p2(struct intelfb_info *dinfo, int dpll, + int *o_p1, int *o_p2) +{ + int p1, p2; + + if (IS_I9XX(dinfo)) { + if (dpll & DPLL_P1_FORCE_DIV2) + p1 = 1; + else + p1 = (dpll >> DPLL_P1_SHIFT) & 0xff; + p1 = ffs(p1); + + p2 = (dpll >> DPLL_I9XX_P2_SHIFT) & DPLL_P2_MASK; + } else { + if (dpll & DPLL_P1_FORCE_DIV2) + p1 = 0; + else + p1 = (dpll >> DPLL_P1_SHIFT) & DPLL_P1_MASK; + p2 = (dpll >> DPLL_P2_SHIFT) & DPLL_P2_MASK; + } + + *o_p1 = p1; + *o_p2 = p2; +} +#endif + + +void intelfbhw_print_hw_state(struct intelfb_info *dinfo, + struct intelfb_hwstate *hw) +{ +#if REGDUMP + int i, m1, m2, n, p1, p2; + int index = dinfo->pll_index; DBG_MSG("intelfbhw_print_hw_state\n"); - if (!hw || !dinfo) + if (!hw) 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); + 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; + + intelfbhw_get_p1p2(dinfo, hw->vga_pd, &p1, &p2); + 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)); + m1, m2, n, p1, p2); + printk(" VGA0: clock is %d\n", + calc_vclock(index, m1, m2, n, p1, p2, 0)); 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; + + intelfbhw_get_p1p2(dinfo, hw->vga_pd, &p1, &p2); 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)); + m1, m2, n, p1, p2); + printk(" VGA1: clock is %d\n", + calc_vclock(index, m1, m2, n, p1, p2, 0)); printk(" DPLL_A: 0x%08x\n", hw->dpll_a); printk(" DPLL_B: 0x%08x\n", hw->dpll_b); @@ -598,34 +753,32 @@ intelfbhw_print_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw) 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; + + intelfbhw_get_p1p2(dinfo, hw->dpll_a, &p1, &p2); + 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)); + m1, m2, n, p1, p2); + printk(" PLLA0: clock is %d\n", + calc_vclock(index, m1, m2, n, p1, p2, 0)); 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; + + intelfbhw_get_p1p2(dinfo, hw->dpll_a, &p1, &p2); + 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)); + m1, m2, n, p1, p2); + printk(" PLLA1: clock is %d\n", + calc_vclock(index, m1, m2, n, p1, p2, 0)); #if 0 printk(" PALETTE_A:\n"); for (i = 0; i < PALETTE_8_ENTRIES) - printk(" %3d: 0x%08x\n", i, hw->palette_a[i]; + 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]; + printk(" %3d: 0x%08x\n", i, hw->palette_b[i]); #endif printk(" HTOTAL_A: 0x%08x\n", hw->htotal_a); @@ -700,79 +853,94 @@ intelfbhw_print_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw) } for (i = 0; i < 3; i++) { printk(" SWF3%d 0x%08x\n", i, - hw->swf3x[i]); + hw->swf3x[i]); } for (i = 0; i < 8; i++) printk(" FENCE%d 0x%08x\n", i, - hw->fence[i]); + hw->fence[i]); printk(" INSTPM 0x%08x\n", hw->instpm); printk(" MEM_MODE 0x%08x\n", hw->mem_mode); printk(" FW_BLC_0 0x%08x\n", hw->fw_blc_0); printk(" FW_BLC_1 0x%08x\n", hw->fw_blc_1); + printk(" HWSTAM 0x%04x\n", hw->hwstam); + printk(" IER 0x%04x\n", hw->ier); + printk(" IIR 0x%04x\n", hw->iir); + printk(" IMR 0x%04x\n", hw->imr); printk("hw state dump end\n"); #endif } + + /* Split the M parameter into M1 and M2. */ -static int -splitm(int index, unsigned int m, unsigned int *retm1, unsigned int *retm2) +static int splitm(int index, unsigned int m, unsigned int *retm1, + unsigned int *retm2) { int m1, m2; - - m1 = (m - 2 - (plls[index].min_m1 + plls[index].max_m2) / 2) / 5 - 2; - if (m1 < plls[index].min_m1) - m1 = plls[index].min_m1; - if (m1 > plls[index].max_m1) - m1 = plls[index].max_m1; - m2 = m - 5 * (m1 + 2) - 2; - if (m2 < plls[index].min_m2 || m2 > plls[index].max_m2 || m2 >= m1) { - return 1; - } else { - *retm1 = (unsigned int)m1; - *retm2 = (unsigned int)m2; - return 0; + int testm; + struct pll_min_max *pll = &plls[index]; + + /* no point optimising too much - brute force m */ + for (m1 = pll->min_m1; m1 < pll->max_m1 + 1; m1++) { + for (m2 = pll->min_m2; m2 < pll->max_m2 + 1; m2++) { + testm = (5 * (m1 + 2)) + (m2 + 2); + if (testm == m) { + *retm1 = (unsigned int)m1; + *retm2 = (unsigned int)m2; + return 0; + } + } } + return 1; } /* Split the P parameter into P1 and P2. */ -static int -splitp(int index, unsigned int p, unsigned int *retp1, unsigned int *retp2) +static int splitp(int index, unsigned int p, unsigned int *retp1, + unsigned int *retp2) { int p1, p2; + struct pll_min_max *pll = &plls[index]; - if (index==PLLS_I8xx) - { - if (p % 4 == 0) - p2 = 1; - else - p2 = 0; + if (index == PLLS_I9xx) { + p2 = (p % 10) ? 1 : 0; + + p1 = p / (p2 ? 5 : 10); + + *retp1 = (unsigned int)p1; + *retp2 = (unsigned int)p2; + return 0; + } + + if (p % 4 == 0) + p2 = 1; + else + p2 = 0; + p1 = (p / (1 << (p2 + 1))) - 2; + if (p % 4 == 0 && p1 < pll->min_p1) { + p2 = 0; p1 = (p / (1 << (p2 + 1))) - 2; - if (p % 4 == 0 && p1 < plls[index].min_p1) { - p2 = 0; - p1 = (p / (1 << (p2 + 1))) - 2; - } - if (p1 < plls[index].min_p1 || p1 > plls[index].max_p1 || (p1 + 2) * (1 << (p2 + 1)) != p) { - return 1; - } else { - *retp1 = (unsigned int)p1; - *retp2 = (unsigned int)p2; - return 0; - } } - return 1; + if (p1 < pll->min_p1 || p1 > pll->max_p1 || + (p1 + 2) * (1 << (p2 + 1)) != p) { + return 1; + } else { + *retp1 = (unsigned int)p1; + *retp2 = (unsigned int)p2; + return 0; + } } -static int -calc_pll_params(int index, int clock, u32 *retm1, u32 *retm2, u32 *retn, u32 *retp1, - u32 *retp2, u32 *retclock) +static int calc_pll_params(int index, int clock, u32 *retm1, u32 *retm2, + u32 *retn, u32 *retp1, u32 *retp2, u32 *retclock) { - u32 m1, m2, n, p1, p2, n1; - u32 f_vco, p, p_best = 0, m, f_out; + u32 m1, m2, n, p1, p2, n1, testm; + u32 f_vco, p, p_best = 0, m, f_out = 0; u32 err_max, err_target, err_best = 10000000; u32 n_best = 0, m_best = 0, f_best, f_err; - u32 p_min, p_max, p_inc, div_min, div_max; + u32 p_min, p_max, p_inc, div_max; + struct pll_min_max *pll = &plls[index]; /* Accept 0.5% difference, but aim for 0.1% */ err_max = 5 * clock / 1000; @@ -780,19 +948,15 @@ calc_pll_params(int index, int clock, u32 *retm1, u32 *retm2, u32 *retn, u32 *re DBG_MSG("Clock is %d\n", clock); - div_max = plls[index].max_vco_freq / clock; - div_min = ROUND_UP_TO(plls[index].min_vco_freq, clock) / clock; + div_max = pll->max_vco / clock; - if (clock <= plls[index].p_transition_clock) - p_inc = 4; - else - p_inc = 2; - p_min = ROUND_UP_TO(div_min, p_inc); + p_inc = (clock <= pll->p_transition_clk) ? pll->p_inc_lo : pll->p_inc_hi; + p_min = p_inc; p_max = ROUND_DOWN_TO(div_max, p_inc); - if (p_min < plls[index].min_p) - p_min = 4; - if (p_max > plls[index].max_p) - p_max = 128; + if (p_min < pll->min_p) + p_min = pll->min_p; + if (p_max > pll->max_p) + p_max = pll->max_p; DBG_MSG("p range is %d-%d (%d)\n", p_min, p_max, p_inc); @@ -803,35 +967,37 @@ calc_pll_params(int index, int clock, u32 *retm1, u32 *retm2, u32 *retn, u32 *re p += p_inc; continue; } - n = plls[index].min_n; + n = pll->min_n; f_vco = clock * p; do { - m = ROUND_UP_TO(f_vco * n, PLL_REFCLK) / PLL_REFCLK; - if (m < plls[index].min_m) - m = plls[index].min_m; - if (m > plls[index].max_m) - m = plls[index].max_m; - f_out = CALC_VCLOCK3(m, n, p); - if (splitm(index, m, &m1, &m2)) { - WRN_MSG("cannot split m = %d\n", m); - n++; - continue; - } - if (clock > f_out) - f_err = clock - f_out; - else - f_err = f_out - clock; - - if (f_err < err_best) { - m_best = m; - n_best = n; - p_best = p; - f_best = f_out; - err_best = f_err; + m = ROUND_UP_TO(f_vco * n, pll->ref_clk) / pll->ref_clk; + if (m < pll->min_m) + m = pll->min_m + 1; + if (m > pll->max_m) + m = pll->max_m - 1; + for (testm = m - 1; testm <= m; testm++) { + f_out = calc_vclock3(index, testm, n, p); + if (splitm(index, testm, &m1, &m2)) { + WRN_MSG("cannot split m = %d\n", + testm); + continue; + } + if (clock > f_out) + f_err = clock - f_out; + else/* slightly bias the error for bigger clocks */ + f_err = f_out - clock + 1; + + if (f_err < err_best) { + m_best = testm; + n_best = n; + p_best = p; + f_best = f_out; + err_best = f_err; + } } n++; - } while ((n <= plls[index].max_n) && (f_out >= clock)); + } while ((n <= pll->max_n) && (f_out >= clock)); p += p_inc; } while ((p <= p_max)); @@ -849,20 +1015,21 @@ calc_pll_params(int index, int clock, u32 *retm1, u32 *retm2, u32 *retn, u32 *re DBG_MSG("m, n, p: %d (%d,%d), %d (%d), %d (%d,%d), " "f: %d (%d), VCO: %d\n", m, m1, m2, n, n1, p, p1, p2, - CALC_VCLOCK3(m, n, p), CALC_VCLOCK(m1, m2, n1, p1, p2), - CALC_VCLOCK3(m, n, p) * p); + calc_vclock3(index, m, n, p), + calc_vclock(index, m1, m2, n1, p1, p2, 0), + calc_vclock3(index, m, n, p) * p); *retm1 = m1; *retm2 = m2; *retn = n1; *retp1 = p1; *retp2 = p2; - *retclock = CALC_VCLOCK(m1, m2, n1, p1, p2); + *retclock = calc_vclock(index, m1, m2, n1, p1, p2, 0); return 0; } -static __inline__ int -check_overflow(u32 value, u32 limit, const char *description) +static __inline__ int check_overflow(u32 value, u32 limit, + const char *description) { if (value > limit) { WRN_MSG("%s value %d exceeds limit %d\n", @@ -873,29 +1040,24 @@ check_overflow(u32 value, u32 limit, const char *description) } /* It is assumed that hw is filled in with the initial state information. */ -int -intelfbhw_mode_to_hw(struct intelfb_info *dinfo, struct intelfb_hwstate *hw, - struct fb_var_screeninfo *var) +int intelfbhw_mode_to_hw(struct intelfb_info *dinfo, + struct intelfb_hwstate *hw, + struct fb_var_screeninfo *var) { - int pipe = PIPE_A; + int pipe = intelfbhw_active_pipe(hw); u32 *dpll, *fp0, *fp1; u32 m1, m2, n, p1, p2, clock_target, clock; u32 hsync_start, hsync_end, hblank_start, hblank_end, htotal, hactive; u32 vsync_start, vsync_end, vblank_start, vblank_end, vtotal, vactive; u32 vsync_pol, hsync_pol; u32 *vs, *vb, *vt, *hs, *hb, *ht, *ss, *pipe_conf; + u32 stride_alignment; DBG_MSG("intelfbhw_mode_to_hw\n"); /* Disable VGA */ hw->vgacntrl |= VGA_DISABLE; - /* Check whether pipe A or pipe B is enabled. */ - if (hw->pipe_a_conf & PIPECONF_ENABLE) - pipe = PIPE_A; - else if (hw->pipe_b_conf & PIPECONF_ENABLE) - pipe = PIPE_B; - /* Set which pipe's registers will be set. */ if (pipe == PIPE_B) { dpll = &hw->dpll_b; @@ -953,7 +1115,8 @@ intelfbhw_mode_to_hw(struct intelfb_info *dinfo, struct intelfb_hwstate *hw, /* Desired clock in kHz */ clock_target = 1000000000 / var->pixclock; - if (calc_pll_params(PLLS_I8xx, clock_target, &m1, &m2, &n, &p1, &p2, &clock)) { + if (calc_pll_params(dinfo->pll_index, clock_target, &m1, &m2, + &n, &p1, &p2, &clock)) { WRN_MSG("calc_pll_params failed\n"); return 1; } @@ -973,7 +1136,13 @@ intelfbhw_mode_to_hw(struct intelfb_info *dinfo, struct intelfb_hwstate *hw, *dpll &= ~DPLL_P1_FORCE_DIV2; *dpll &= ~((DPLL_P2_MASK << DPLL_P2_SHIFT) | (DPLL_P1_MASK << DPLL_P1_SHIFT)); - *dpll |= (p2 << DPLL_P2_SHIFT) | (p1 << DPLL_P1_SHIFT); + + if (IS_I9XX(dinfo)) { + *dpll |= (p2 << DPLL_I9XX_P2_SHIFT); + *dpll |= (1 << (p1 - 1)) << DPLL_P1_SHIFT; + } else + *dpll |= (p2 << DPLL_P2_SHIFT) | (p1 << DPLL_P1_SHIFT); + *fp0 = (n << FP_N_DIVISOR_SHIFT) | (m1 << FP_M1_DIVISOR_SHIFT) | (m2 << FP_M2_DIVISOR_SHIFT); @@ -1016,6 +1185,8 @@ intelfbhw_mode_to_hw(struct intelfb_info *dinfo, struct intelfb_hwstate *hw, hblank_end); vactive = var->yres; + if (var->vmode & FB_VMODE_INTERLACED) + vactive--; /* the chip adds 2 halflines automatically */ vsync_start = vactive + var->lower_margin; vsync_end = vsync_start + var->vsync_len; vtotal = vsync_end + var->upper_margin; @@ -1078,7 +1249,7 @@ intelfbhw_mode_to_hw(struct intelfb_info *dinfo, struct intelfb_hwstate *hw, *ss = (hactive << SRC_SIZE_HORIZ_SHIFT) | (vactive << SRC_SIZE_VERT_SHIFT); - hw->disp_a_stride = var->xres_virtual * var->bits_per_pixel / 8; + hw->disp_a_stride = dinfo->pitch; DBG_MSG("pitch is %d\n", hw->disp_a_stride); hw->disp_a_base = hw->disp_a_stride * var->yoffset + @@ -1087,32 +1258,39 @@ intelfbhw_mode_to_hw(struct intelfb_info *dinfo, struct intelfb_hwstate *hw, hw->disp_a_base += dinfo->fb.offset << 12; /* Check stride alignment. */ - if (hw->disp_a_stride % STRIDE_ALIGNMENT != 0) { + stride_alignment = IS_I9XX(dinfo) ? STRIDE_ALIGNMENT_I9XX : + STRIDE_ALIGNMENT; + if (hw->disp_a_stride % stride_alignment != 0) { WRN_MSG("display stride %d has bad alignment %d\n", - hw->disp_a_stride, STRIDE_ALIGNMENT); + hw->disp_a_stride, stride_alignment); return 1; } /* Set the palette to 8-bit mode. */ *pipe_conf &= ~PIPECONF_GAMMA; + + if (var->vmode & FB_VMODE_INTERLACED) + *pipe_conf |= PIPECONF_INTERLACE_W_FIELD_INDICATION; + else + *pipe_conf &= ~PIPECONF_INTERLACE_MASK; + return 0; } /* Program a (non-VGA) video mode. */ -int -intelfbhw_program_mode(struct intelfb_info *dinfo, - const struct intelfb_hwstate *hw, int blank) +int intelfbhw_program_mode(struct intelfb_info *dinfo, + const struct intelfb_hwstate *hw, int blank) { - int pipe = PIPE_A; u32 tmp; const u32 *dpll, *fp0, *fp1, *pipe_conf; const u32 *hs, *ht, *hb, *vs, *vt, *vb, *ss; - u32 dpll_reg, fp0_reg, fp1_reg, pipe_conf_reg; + u32 dpll_reg, fp0_reg, fp1_reg, pipe_conf_reg, pipe_stat_reg; u32 hsync_reg, htotal_reg, hblank_reg; u32 vsync_reg, vtotal_reg, vblank_reg; u32 src_size_reg; + u32 count, tmp_val[3]; - /* Assume single pipe, display plane A, analog CRT. */ + /* Assume single pipe */ #if VERBOSE > 0 DBG_MSG("intelfbhw_program_mode\n"); @@ -1123,15 +1301,9 @@ intelfbhw_program_mode(struct intelfb_info *dinfo, tmp |= VGA_DISABLE; OUTREG(VGACNTRL, tmp); - /* Check whether pipe A or pipe B is enabled. */ - if (hw->pipe_a_conf & PIPECONF_ENABLE) - pipe = PIPE_A; - else if (hw->pipe_b_conf & PIPECONF_ENABLE) - pipe = PIPE_B; - - dinfo->pipe = pipe; + dinfo->pipe = intelfbhw_active_pipe(hw); - if (pipe == PIPE_B) { + if (dinfo->pipe == PIPE_B) { dpll = &hw->dpll_b; fp0 = &hw->fpb0; fp1 = &hw->fpb1; @@ -1147,6 +1319,7 @@ intelfbhw_program_mode(struct intelfb_info *dinfo, fp0_reg = FPB0; fp1_reg = FPB1; pipe_conf_reg = PIPEBCONF; + pipe_stat_reg = PIPEBSTAT; hsync_reg = HSYNC_B; htotal_reg = HTOTAL_B; hblank_reg = HBLANK_B; @@ -1170,6 +1343,7 @@ intelfbhw_program_mode(struct intelfb_info *dinfo, fp0_reg = FPA0; fp1_reg = FPA1; pipe_conf_reg = PIPEACONF; + pipe_stat_reg = PIPEASTAT; hsync_reg = HSYNC_A; htotal_reg = HTOTAL_A; hblank_reg = HBLANK_A; @@ -1179,6 +1353,27 @@ intelfbhw_program_mode(struct intelfb_info *dinfo, src_size_reg = SRC_SIZE_A; } + /* turn off pipe */ + tmp = INREG(pipe_conf_reg); + tmp &= ~PIPECONF_ENABLE; + OUTREG(pipe_conf_reg, tmp); + + count = 0; + do { + tmp_val[count % 3] = INREG(PIPEA_DSL); + if ((tmp_val[0] == tmp_val[1]) && (tmp_val[1] == tmp_val[2])) + break; + count++; + udelay(1); + if (count % 200 == 0) { + tmp = INREG(pipe_conf_reg); + tmp &= ~PIPECONF_ENABLE; + OUTREG(pipe_conf_reg, tmp); + } + } while (count < 2000); + + OUTREG(ADPA, INREG(ADPA) & ~ADPA_DAC_ENABLE); + /* Disable planes A and B. */ tmp = INREG(DSPACNTR); tmp &= ~DISPPLANE_PLANE_ENABLE; @@ -1187,51 +1382,65 @@ intelfbhw_program_mode(struct intelfb_info *dinfo, tmp &= ~DISPPLANE_PLANE_ENABLE; OUTREG(DSPBCNTR, tmp); - /* Wait for vblank. For now, just wait for a 50Hz cycle (20ms)) */ + /* Wait for vblank. For now, just wait for a 50Hz cycle (20ms)) */ mdelay(20); + OUTREG(DVOB, INREG(DVOB) & ~PORT_ENABLE); + OUTREG(DVOC, INREG(DVOC) & ~PORT_ENABLE); + OUTREG(ADPA, INREG(ADPA) & ~ADPA_DAC_ENABLE); + /* Disable Sync */ tmp = INREG(ADPA); tmp &= ~ADPA_DPMS_CONTROL_MASK; tmp |= ADPA_DPMS_D3; OUTREG(ADPA, tmp); - /* turn off pipe */ - tmp = INREG(pipe_conf_reg); - tmp &= ~PIPECONF_ENABLE; - OUTREG(pipe_conf_reg, tmp); + /* do some funky magic - xyzzy */ + OUTREG(0x61204, 0xabcd0000); /* turn off PLL */ tmp = INREG(dpll_reg); - dpll_reg &= ~DPLL_VCO_ENABLE; + tmp &= ~DPLL_VCO_ENABLE; OUTREG(dpll_reg, tmp); /* Set PLL parameters */ - OUTREG(dpll_reg, *dpll & ~DPLL_VCO_ENABLE); OUTREG(fp0_reg, *fp0); OUTREG(fp1_reg, *fp1); - /* Set pipe parameters */ - OUTREG(hsync_reg, *hs); - OUTREG(hblank_reg, *hb); - OUTREG(htotal_reg, *ht); - OUTREG(vsync_reg, *vs); - OUTREG(vblank_reg, *vb); - OUTREG(vtotal_reg, *vt); - OUTREG(src_size_reg, *ss); + /* Enable PLL */ + OUTREG(dpll_reg, *dpll); /* Set DVOs B/C */ OUTREG(DVOB, hw->dvob); OUTREG(DVOC, hw->dvoc); + /* undo funky magic */ + OUTREG(0x61204, 0x00000000); + /* Set ADPA */ + OUTREG(ADPA, INREG(ADPA) | ADPA_DAC_ENABLE); OUTREG(ADPA, (hw->adpa & ~(ADPA_DPMS_CONTROL_MASK)) | ADPA_DPMS_D3); - /* Enable PLL */ - tmp = INREG(dpll_reg); - tmp |= DPLL_VCO_ENABLE; - OUTREG(dpll_reg, tmp); + /* Set pipe parameters */ + OUTREG(hsync_reg, *hs); + OUTREG(hblank_reg, *hb); + OUTREG(htotal_reg, *ht); + OUTREG(vsync_reg, *vs); + OUTREG(vblank_reg, *vb); + OUTREG(vtotal_reg, *vt); + OUTREG(src_size_reg, *ss); + switch (dinfo->info->var.vmode & (FB_VMODE_INTERLACED | + FB_VMODE_ODD_FLD_FIRST)) { + case FB_VMODE_INTERLACED | FB_VMODE_ODD_FLD_FIRST: + OUTREG(pipe_stat_reg, 0xFFFF | PIPESTAT_FLD_EVT_ODD_EN); + break; + case FB_VMODE_INTERLACED: /* even lines first */ + OUTREG(pipe_stat_reg, 0xFFFF | PIPESTAT_FLD_EVT_EVEN_EN); + break; + default: /* non-interlaced */ + OUTREG(pipe_stat_reg, 0xFFFF); /* clear all status bits only */ + } /* Enable pipe */ OUTREG(pipe_conf_reg, *pipe_conf | PIPECONF_ENABLE); @@ -1255,7 +1464,7 @@ intelfbhw_program_mode(struct intelfb_info *dinfo, OUTREG(DSPACNTR, hw->disp_a_ctrl|DISPPLANE_PLANE_ENABLE); mdelay(1); - } + } } OUTREG(DSPACNTR, hw->disp_a_ctrl & ~DISPPLANE_PLANE_ENABLE); @@ -1278,8 +1487,25 @@ static void refresh_ring(struct intelfb_info *dinfo); static void reset_state(struct intelfb_info *dinfo); static void do_flush(struct intelfb_info *dinfo); -static int -wait_ring(struct intelfb_info *dinfo, int n) +static u32 get_ring_space(struct intelfb_info *dinfo) +{ + u32 ring_space; + + if (dinfo->ring_tail >= dinfo->ring_head) + ring_space = dinfo->ring.size - + (dinfo->ring_tail - dinfo->ring_head); + else + ring_space = dinfo->ring_head - dinfo->ring_tail; + + if (ring_space > RING_MIN_FREE) + ring_space -= RING_MIN_FREE; + else + ring_space = 0; + + return ring_space; +} + +static int wait_ring(struct intelfb_info *dinfo, int n) { int i = 0; unsigned long end; @@ -1291,19 +1517,12 @@ wait_ring(struct intelfb_info *dinfo, int n) end = jiffies + (HZ * 3); while (dinfo->ring_space < n) { - dinfo->ring_head = (u8 __iomem *)(INREG(PRI_RING_HEAD) & - RING_HEAD_MASK); - if (dinfo->ring_tail + RING_MIN_FREE < - (u32 __iomem) dinfo->ring_head) - dinfo->ring_space = (u32 __iomem) dinfo->ring_head - - (dinfo->ring_tail + RING_MIN_FREE); - else - dinfo->ring_space = (dinfo->ring.size + - (u32 __iomem) dinfo->ring_head) - - (dinfo->ring_tail + RING_MIN_FREE); - if ((u32 __iomem) dinfo->ring_head != last_head) { + dinfo->ring_head = INREG(PRI_RING_HEAD) & RING_HEAD_MASK; + dinfo->ring_space = get_ring_space(dinfo); + + if (dinfo->ring_head != last_head) { end = jiffies + (HZ * 3); - last_head = (u32 __iomem) dinfo->ring_head; + last_head = dinfo->ring_head; } i++; if (time_before(end, jiffies)) { @@ -1328,16 +1547,15 @@ wait_ring(struct intelfb_info *dinfo, int n) return i; } -static void -do_flush(struct intelfb_info *dinfo) { +static void do_flush(struct intelfb_info *dinfo) +{ START_RING(2); OUT_RING(MI_FLUSH | MI_WRITE_DIRTY_STATE | MI_INVALIDATE_MAP_CACHE); OUT_RING(MI_NOOP); ADVANCE_RING(); } -void -intelfbhw_do_sync(struct intelfb_info *dinfo) +void intelfbhw_do_sync(struct intelfb_info *dinfo) { #if VERBOSE > 0 DBG_MSG("intelfbhw_do_sync\n"); @@ -1356,27 +1574,18 @@ intelfbhw_do_sync(struct intelfb_info *dinfo) dinfo->ring_space = dinfo->ring.size - RING_MIN_FREE; } -static void -refresh_ring(struct intelfb_info *dinfo) +static void refresh_ring(struct intelfb_info *dinfo) { #if VERBOSE > 0 DBG_MSG("refresh_ring\n"); #endif - dinfo->ring_head = (u8 __iomem *) (INREG(PRI_RING_HEAD) & - RING_HEAD_MASK); + dinfo->ring_head = INREG(PRI_RING_HEAD) & RING_HEAD_MASK; dinfo->ring_tail = INREG(PRI_RING_TAIL) & RING_TAIL_MASK; - if (dinfo->ring_tail + RING_MIN_FREE < (u32 __iomem)dinfo->ring_head) - dinfo->ring_space = (u32 __iomem) dinfo->ring_head - - (dinfo->ring_tail + RING_MIN_FREE); - else - dinfo->ring_space = (dinfo->ring.size + - (u32 __iomem) dinfo->ring_head) - - (dinfo->ring_tail + RING_MIN_FREE); + dinfo->ring_space = get_ring_space(dinfo); } -static void -reset_state(struct intelfb_info *dinfo) +static void reset_state(struct intelfb_info *dinfo) { int i; u32 tmp; @@ -1406,12 +1615,11 @@ reset_state(struct intelfb_info *dinfo) } /* Stop the 2D engine, and turn off the ring buffer. */ -void -intelfbhw_2d_stop(struct intelfb_info *dinfo) +void intelfbhw_2d_stop(struct intelfb_info *dinfo) { #if VERBOSE > 0 - DBG_MSG("intelfbhw_2d_stop: accel: %d, ring_active: %d\n", dinfo->accel, - dinfo->ring_active); + DBG_MSG("intelfbhw_2d_stop: accel: %d, ring_active: %d\n", + dinfo->accel, dinfo->ring_active); #endif if (!dinfo->accel) @@ -1426,8 +1634,7 @@ intelfbhw_2d_stop(struct intelfb_info *dinfo) * It is assumed that the graphics engine has been stopped by previously * calling intelfb_2d_stop(). */ -void -intelfbhw_2d_start(struct intelfb_info *dinfo) +void intelfbhw_2d_start(struct intelfb_info *dinfo) { #if VERBOSE > 0 DBG_MSG("intelfbhw_2d_start: accel: %d, ring_active: %d\n", @@ -1451,9 +1658,8 @@ intelfbhw_2d_start(struct intelfb_info *dinfo) } /* 2D fillrect (solid fill or invert) */ -void -intelfbhw_do_fillrect(struct intelfb_info *dinfo, u32 x, u32 y, u32 w, u32 h, - u32 color, u32 pitch, u32 bpp, u32 rop) +void intelfbhw_do_fillrect(struct intelfb_info *dinfo, u32 x, u32 y, u32 w, + u32 h, u32 color, u32 pitch, u32 bpp, u32 rop) { u32 br00, br09, br13, br14, br16; @@ -1542,9 +1748,9 @@ intelfbhw_do_bitblt(struct intelfb_info *dinfo, u32 curx, u32 cury, ADVANCE_RING(); } -int -intelfbhw_do_drawglyph(struct intelfb_info *dinfo, u32 fg, u32 bg, u32 w, - u32 h, const u8* cdat, u32 x, u32 y, u32 pitch, u32 bpp) +int intelfbhw_do_drawglyph(struct intelfb_info *dinfo, u32 fg, u32 bg, u32 w, + u32 h, const u8* cdat, u32 x, u32 y, u32 pitch, + u32 bpp) { int nbytes, ndwords, pad, tmp; u32 br00, br09, br13, br18, br19, br22, br23; @@ -1631,8 +1837,7 @@ intelfbhw_do_drawglyph(struct intelfb_info *dinfo, u32 fg, u32 bg, u32 w, } /* HW cursor functions. */ -void -intelfbhw_cursor_init(struct intelfb_info *dinfo) +void intelfbhw_cursor_init(struct intelfb_info *dinfo) { u32 tmp; @@ -1640,7 +1845,7 @@ intelfbhw_cursor_init(struct intelfb_info *dinfo) DBG_MSG("intelfbhw_cursor_init\n"); #endif - if (dinfo->mobile) { + if (dinfo->mobile || IS_I9XX(dinfo)) { if (!dinfo->cursor.physical) return; tmp = INREG(CURSOR_A_CONTROL); @@ -1663,8 +1868,7 @@ intelfbhw_cursor_init(struct intelfb_info *dinfo) } } -void -intelfbhw_cursor_hide(struct intelfb_info *dinfo) +void intelfbhw_cursor_hide(struct intelfb_info *dinfo) { u32 tmp; @@ -1673,7 +1877,7 @@ intelfbhw_cursor_hide(struct intelfb_info *dinfo) #endif dinfo->cursor_on = 0; - if (dinfo->mobile) { + if (dinfo->mobile || IS_I9XX(dinfo)) { if (!dinfo->cursor.physical) return; tmp = INREG(CURSOR_A_CONTROL); @@ -1689,8 +1893,7 @@ intelfbhw_cursor_hide(struct intelfb_info *dinfo) } } -void -intelfbhw_cursor_show(struct intelfb_info *dinfo) +void intelfbhw_cursor_show(struct intelfb_info *dinfo) { u32 tmp; @@ -1703,7 +1906,7 @@ intelfbhw_cursor_show(struct intelfb_info *dinfo) if (dinfo->cursor_blanked) return; - if (dinfo->mobile) { + if (dinfo->mobile || IS_I9XX(dinfo)) { if (!dinfo->cursor.physical) return; tmp = INREG(CURSOR_A_CONTROL); @@ -1719,8 +1922,7 @@ intelfbhw_cursor_show(struct intelfb_info *dinfo) } } -void -intelfbhw_cursor_setpos(struct intelfb_info *dinfo, int x, int y) +void intelfbhw_cursor_setpos(struct intelfb_info *dinfo, int x, int y) { u32 tmp; @@ -1729,18 +1931,20 @@ intelfbhw_cursor_setpos(struct intelfb_info *dinfo, int x, int y) #endif /* - * Sets the position. The coordinates are assumed to already - * have any offset adjusted. Assume that the cursor is never + * Sets the position. The coordinates are assumed to already + * have any offset adjusted. Assume that the cursor is never * completely off-screen, and that x, y are always >= 0. */ tmp = ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT) | ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); OUTREG(CURSOR_A_POSITION, tmp); + + if (IS_I9XX(dinfo)) + OUTREG(CURSOR_A_BASEADDR, dinfo->cursor.physical); } -void -intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg, u32 fg) +void intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg, u32 fg) { #if VERBOSE > 0 DBG_MSG("intelfbhw_cursor_setcolor\n"); @@ -1752,9 +1956,8 @@ intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg, u32 fg) OUTREG(CURSOR_A_PALETTE3, bg & CURSOR_PALETTE_MASK); } -void -intelfbhw_cursor_load(struct intelfb_info *dinfo, int width, int height, - u8 *data) +void intelfbhw_cursor_load(struct intelfb_info *dinfo, int width, int height, + u8 *data) { u8 __iomem *addr = (u8 __iomem *)dinfo->cursor.virtual; int i, j, w = width / 8; @@ -1782,8 +1985,8 @@ intelfbhw_cursor_load(struct intelfb_info *dinfo, int width, int height, } } -void -intelfbhw_cursor_reset(struct intelfb_info *dinfo) { +void intelfbhw_cursor_reset(struct intelfb_info *dinfo) +{ u8 __iomem *addr = (u8 __iomem *)dinfo->cursor.virtual; int i, j; @@ -1802,3 +2005,117 @@ intelfbhw_cursor_reset(struct intelfb_info *dinfo) { addr += 16; } } + +static irqreturn_t intelfbhw_irq(int irq, void *dev_id) +{ + u16 tmp; + struct intelfb_info *dinfo = dev_id; + + spin_lock(&dinfo->int_lock); + + tmp = INREG16(IIR); + if (dinfo->info->var.vmode & FB_VMODE_INTERLACED) + tmp &= PIPE_A_EVENT_INTERRUPT; + else + tmp &= VSYNC_PIPE_A_INTERRUPT; /* non-interlaced */ + + if (tmp == 0) { + spin_unlock(&dinfo->int_lock); + return IRQ_RETVAL(0); /* not us */ + } + + /* clear status bits 0-15 ASAP and don't touch bits 16-31 */ + OUTREG(PIPEASTAT, INREG(PIPEASTAT)); + + OUTREG16(IIR, tmp); + if (dinfo->vsync.pan_display) { + dinfo->vsync.pan_display = 0; + OUTREG(DSPABASE, dinfo->vsync.pan_offset); + } + + dinfo->vsync.count++; + wake_up_interruptible(&dinfo->vsync.wait); + + spin_unlock(&dinfo->int_lock); + + return IRQ_RETVAL(1); +} + +int intelfbhw_enable_irq(struct intelfb_info *dinfo) +{ + u16 tmp; + if (!test_and_set_bit(0, &dinfo->irq_flags)) { + if (request_irq(dinfo->pdev->irq, intelfbhw_irq, IRQF_SHARED, + "intelfb", dinfo)) { + clear_bit(0, &dinfo->irq_flags); + return -EINVAL; + } + + spin_lock_irq(&dinfo->int_lock); + OUTREG16(HWSTAM, 0xfffe); /* i830 DRM uses ffff */ + OUTREG16(IMR, 0); + } else + spin_lock_irq(&dinfo->int_lock); + + if (dinfo->info->var.vmode & FB_VMODE_INTERLACED) + tmp = PIPE_A_EVENT_INTERRUPT; + else + tmp = VSYNC_PIPE_A_INTERRUPT; /* non-interlaced */ + if (tmp != INREG16(IER)) { + DBG_MSG("changing IER to 0x%X\n", tmp); + OUTREG16(IER, tmp); + } + + spin_unlock_irq(&dinfo->int_lock); + return 0; +} + +void intelfbhw_disable_irq(struct intelfb_info *dinfo) +{ + if (test_and_clear_bit(0, &dinfo->irq_flags)) { + if (dinfo->vsync.pan_display) { + dinfo->vsync.pan_display = 0; + OUTREG(DSPABASE, dinfo->vsync.pan_offset); + } + spin_lock_irq(&dinfo->int_lock); + OUTREG16(HWSTAM, 0xffff); + OUTREG16(IMR, 0xffff); + OUTREG16(IER, 0x0); + + OUTREG16(IIR, INREG16(IIR)); /* clear IRQ requests */ + spin_unlock_irq(&dinfo->int_lock); + + free_irq(dinfo->pdev->irq, dinfo); + } +} + +int intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe) +{ + struct intelfb_vsync *vsync; + unsigned int count; + int ret; + + switch (pipe) { + case 0: + vsync = &dinfo->vsync; + break; + default: + return -ENODEV; + } + + ret = intelfbhw_enable_irq(dinfo); + if (ret) + return ret; + + count = vsync->count; + ret = wait_event_interruptible_timeout(vsync->wait, + count != vsync->count, HZ / 10); + if (ret < 0) + return ret; + if (ret == 0) { + DBG_MSG("wait_for_vsync timed out!\n"); + return -ETIMEDOUT; + } + + return 0; +}