X-Git-Url: http://ftp.safe.ca/?p=safe%2Fjmp%2Flinux-2.6;a=blobdiff_plain;f=drivers%2Fvideo%2Fintelfb%2Fintelfbhw.c;h=38065cf94ac4cf5dbaf195233871712af4607ab6;hp=73e1902dc3bb0584faa9f2100a51f586a7f08533;hb=49c39b4953e545ce3b5957cce22e1ade01c6e642;hpb=9a90603f65dd5046ddcd586158abcad7784892b6 diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c index 73e1902..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,14 +39,10 @@ #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; }; @@ -56,13 +50,21 @@ struct pll_min_max { #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, 4, 22 }, //I8xx - { 75, 120, 10, 20, 5, 9, 4, 7, 5, 80, 1, 8, 930000, 2800000, 200000, 10, 5 } //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, struct intelfb_info *dinfo) +int intelfbhw_get_chipset(struct pci_dev *pdev, struct intelfb_info *dinfo) { u32 tmp; if (!pdev || !dinfo) @@ -81,6 +83,11 @@ intelfbhw_get_chipset(struct pci_dev *pdev, struct intelfb_info *dinfo) 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; dinfo->mobile = 1; @@ -140,23 +147,41 @@ intelfbhw_get_chipset(struct pci_dev *pdev, struct intelfb_info *dinfo) 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; } @@ -164,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"); @@ -194,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"); @@ -228,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; @@ -245,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"; @@ -261,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; @@ -296,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) { @@ -323,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; @@ -345,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; @@ -377,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; @@ -409,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) | @@ -429,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; @@ -562,6 +645,11 @@ 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; } @@ -570,96 +658,120 @@ static int calc_vclock3(int index, int m, int n, int p) { if (p == 0 || n == 0) return 0; - return PLL_REFCLK * m / n / p; + return plls[index].ref_clk * m / n / p; } -static int calc_vclock(int index, int m1, int m2, int n, int p1, int p2) +static int calc_vclock(int index, int m1, int m2, int n, int p1, int p2, + int lvds) { - switch(index) - { - case PLLS_I9xx: - if (p1 == 0) - return 0; - return ((PLL_REFCLK * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / - ((p1)) * (p2 ? 10 : 5))); - case PLLS_I8xx: - default: - return ((PLL_REFCLK * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / - ((p1+2) * (1 << (p2 + 1))))); + 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 +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) + +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(index, m1, m2, n, p1, p2)); - + 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(index, 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); 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; + + 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(index, 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(index, 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) @@ -752,6 +864,10 @@ intelfbhw_print_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw) 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 } @@ -759,15 +875,17 @@ intelfbhw_print_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw) /* 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; int testm; + struct pll_min_max *pll = &plls[index]; + /* no point optimising too much - brute force m */ - for (m1 = plls[index].min_m1; m1 < plls[index].max_m1+1; m1++) { - for (m2 = plls[index].min_m2; m2 < plls[index].max_m2+1; m2++) { - testm = ( 5 * ( m1 + 2 )) + (m2 + 2); + 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; @@ -779,64 +897,50 @@ splitm(int index, unsigned int m, unsigned int *retm1, unsigned int *retm2) } /* 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_I9xx) { - switch (p) { - case 10: - p1 = 2; - p2 = 0; - break; - case 20: - p1 = 1; - p2 = 0; - break; - default: - p1 = (p / 10) + 1; - p2 = 0; - break; - } - + p2 = (p % 10) ? 1 : 0; + + p1 = p / (p2 ? 5 : 10); + *retp1 = (unsigned int)p1; *retp2 = (unsigned int)p2; return 0; } - if (index == PLLS_I8xx) { - if (p % 4 == 0) - p2 = 1; - else - p2 = 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, 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; @@ -844,31 +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; - if (index == PLLS_I9xx) - div_min = 5; - else - 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 = plls[index].p_inc_lo; - else - p_inc = plls[index].p_inc_hi; - 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 = plls[index].min_p; - if (p_max > plls[index].max_p) - p_max = plls[index].max_p; - - if (clock < PLL_REFCLK && index == PLLS_I9xx) { - p_min = 10; - p_max = 20; - /* this makes 640x480 work it really shouldn't - - SOMEONE WITHOUT DOCS WOZ HERE */ - if (clock < 30000) - clock *= 4; - } + 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); @@ -879,29 +967,29 @@ 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 + 1; - if (m > plls[index].max_m) - m = plls[index].max_m - 1; + 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, m, n, p); - if (splitm(index, m, &m1, &m2)) { - WRN_MSG("cannot split m = %d\n", m); - n++; + 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 = m; + m_best = testm; n_best = n; p_best = p; f_best = f_out; @@ -909,7 +997,7 @@ calc_pll_params(int index, int clock, u32 *retm1, u32 *retm2, u32 *retn, u32 *re } } 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)); @@ -928,20 +1016,20 @@ calc_pll_params(int index, int clock, u32 *retm1, u32 *retm2, u32 *retn, u32 *re "f: %d (%d), VCO: %d\n", m, m1, m2, n, n1, p, p1, p2, calc_vclock3(index, m, n, p), - calc_vclock(index, m1, m2, n1, p1, p2), + 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(index, 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", @@ -952,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; @@ -1032,7 +1115,7 @@ 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(dinfo->pll_index, clock_target, &m1, &m2, + if (calc_pll_params(dinfo->pll_index, clock_target, &m1, &m2, &n, &p1, &p2, &clock)) { WRN_MSG("calc_pll_params failed\n"); return 1; @@ -1053,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); @@ -1096,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; @@ -1158,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 + @@ -1167,33 +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"); @@ -1204,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 = intelfbhw_active_pipe(hw); - dinfo->pipe = pipe; - - if (pipe == PIPE_B) { + if (dinfo->pipe == PIPE_B) { dpll = &hw->dpll_b; fp0 = &hw->fpb0; fp1 = &hw->fpb1; @@ -1228,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; @@ -1251,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; @@ -1264,20 +1357,20 @@ intelfbhw_program_mode(struct intelfb_info *dinfo, tmp = INREG(pipe_conf_reg); tmp &= ~PIPECONF_ENABLE; OUTREG(pipe_conf_reg, tmp); - + count = 0; do { - tmp_val[count%3] = INREG(0x70000); - 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); + 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); @@ -1289,9 +1382,13 @@ 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; @@ -1303,18 +1400,15 @@ intelfbhw_program_mode(struct intelfb_info *dinfo, /* 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); /* Enable PLL */ - tmp = INREG(dpll_reg); - tmp |= DPLL_VCO_ENABLE; - OUTREG(dpll_reg, tmp); + OUTREG(dpll_reg, *dpll); /* Set DVOs B/C */ OUTREG(DVOB, hw->dvob); @@ -1336,6 +1430,17 @@ intelfbhw_program_mode(struct intelfb_info *dinfo, 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); @@ -1359,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); @@ -1382,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; @@ -1395,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)) { @@ -1432,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"); @@ -1460,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; @@ -1510,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) @@ -1530,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", @@ -1555,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; @@ -1646,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; @@ -1735,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; @@ -1744,7 +1845,7 @@ intelfbhw_cursor_init(struct intelfb_info *dinfo) DBG_MSG("intelfbhw_cursor_init\n"); #endif - if (dinfo->mobile || IS_I9xx(dinfo)) { + if (dinfo->mobile || IS_I9XX(dinfo)) { if (!dinfo->cursor.physical) return; tmp = INREG(CURSOR_A_CONTROL); @@ -1767,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; @@ -1777,7 +1877,7 @@ intelfbhw_cursor_hide(struct intelfb_info *dinfo) #endif dinfo->cursor_on = 0; - if (dinfo->mobile || IS_I9xx(dinfo)) { + if (dinfo->mobile || IS_I9XX(dinfo)) { if (!dinfo->cursor.physical) return; tmp = INREG(CURSOR_A_CONTROL); @@ -1793,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; @@ -1807,7 +1906,7 @@ intelfbhw_cursor_show(struct intelfb_info *dinfo) if (dinfo->cursor_blanked) return; - if (dinfo->mobile || IS_I9xx(dinfo)) { + if (dinfo->mobile || IS_I9XX(dinfo)) { if (!dinfo->cursor.physical) return; tmp = INREG(CURSOR_A_CONTROL); @@ -1823,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; @@ -1833,8 +1931,8 @@ 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. */ @@ -1842,13 +1940,11 @@ intelfbhw_cursor_setpos(struct intelfb_info *dinfo, int x, int y) ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); OUTREG(CURSOR_A_POSITION, tmp); - if (IS_I9xx(dinfo)) { + 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"); @@ -1860,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; @@ -1890,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; @@ -1910,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; +}