diff options
author | Krzysztof Halasa <khc@pm.waw.pl> | 2007-10-16 12:29:34 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-16 20:43:20 +0400 |
commit | 394d3af7ba9e67d630c1c6d2ac1d9c11b318b73e (patch) | |
tree | 7057c8c671e7bc33753830928c82c9c5378cd496 /drivers/video | |
parent | 28ebe4f66beda8f142569d24fe3b168f8a08a6a6 (diff) | |
download | linux-394d3af7ba9e67d630c1c6d2ac1d9c11b318b73e.tar.xz |
Intel FB: more interlaced mode support
Intel FB: allow odd- and even-field-first in interlaced modes, and
proper sync to vertical retrace
Signed-off-by: Krzysztof Halasa <khc@pm.waw.pl>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Cc: <sylvain.meyer@worldonline.fr>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/intelfb/intelfbhw.c | 91 | ||||
-rw-r--r-- | drivers/video/intelfb/intelfbhw.h | 30 |
2 files changed, 81 insertions, 40 deletions
diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c index a02ee24f3668..2a0e32074f7d 100644 --- a/drivers/video/intelfb/intelfbhw.c +++ b/drivers/video/intelfb/intelfbhw.c @@ -376,7 +376,7 @@ int intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) dinfo->vsync.pan_offset = offset; if ((var->activate & FB_ACTIVATE_VBL) && - !intelfbhw_enable_irq(dinfo, 0)) + !intelfbhw_enable_irq(dinfo)) dinfo->vsync.pan_display = 1; else { dinfo->vsync.pan_display = 0; @@ -1240,7 +1240,7 @@ int intelfbhw_program_mode(struct intelfb_info *dinfo, 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; @@ -1281,6 +1281,7 @@ int 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; @@ -1304,6 +1305,7 @@ int 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; @@ -1390,6 +1392,17 @@ int 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); @@ -1955,71 +1968,72 @@ void intelfbhw_cursor_reset(struct intelfb_info *dinfo) } } -static irqreturn_t -intelfbhw_irq(int irq, void *dev_id) { - int handled = 0; +static irqreturn_t intelfbhw_irq(int irq, void *dev_id) +{ u16 tmp; struct intelfb_info *dinfo = (struct intelfb_info *)dev_id; spin_lock(&dinfo->int_lock); tmp = INREG16(IIR); - tmp &= VSYNC_PIPE_A_INTERRUPT; + 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(handled); + return IRQ_RETVAL(0); /* not us */ } - OUTREG16(IIR, tmp); + /* clear status bits 0-15 ASAP and don't touch bits 16-31 */ + OUTREG(PIPEASTAT, INREG(PIPEASTAT)); - if (tmp & VSYNC_PIPE_A_INTERRUPT) { - dinfo->vsync.count++; - if (dinfo->vsync.pan_display) { - dinfo->vsync.pan_display = 0; - OUTREG(DSPABASE, dinfo->vsync.pan_offset); - } - wake_up_interruptible(&dinfo->vsync.wait); - handled = 1; + 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(handled); + return IRQ_RETVAL(1); } -int -intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable) { - +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)) { + "intelfb", dinfo)) { clear_bit(0, &dinfo->irq_flags); return -EINVAL; } spin_lock_irq(&dinfo->int_lock); - OUTREG16(HWSTAM, 0xfffe); - OUTREG16(IMR, 0x0); - OUTREG16(IER, VSYNC_PIPE_A_INTERRUPT); - spin_unlock_irq(&dinfo->int_lock); - } else if (reenable) { - u16 ier; - + OUTREG16(HWSTAM, 0xfffe); /* i830 DRM uses ffff */ + OUTREG16(IMR, 0); + } else spin_lock_irq(&dinfo->int_lock); - ier = INREG16(IER); - if ((ier & VSYNC_PIPE_A_INTERRUPT)) { - DBG_MSG("someone disabled the IRQ [%08X]\n", ier); - OUTREG(IER, VSYNC_PIPE_A_INTERRUPT); - } - spin_unlock_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) { - +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; @@ -2051,7 +2065,7 @@ int intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe) return -ENODEV; } - ret = intelfbhw_enable_irq(dinfo, 0); + ret = intelfbhw_enable_irq(dinfo); if (ret) return ret; @@ -2061,7 +2075,6 @@ int intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe) if (ret < 0) return ret; if (ret == 0) { - intelfbhw_enable_irq(dinfo, 1); DBG_MSG("wait_for_vsync timed out!\n"); return -ETIMEDOUT; } diff --git a/drivers/video/intelfb/intelfbhw.h b/drivers/video/intelfb/intelfbhw.h index 3cbfcef83361..0b076bac321b 100644 --- a/drivers/video/intelfb/intelfbhw.h +++ b/drivers/video/intelfb/intelfbhw.h @@ -280,6 +280,9 @@ #define PIPEB_DSL 0x71000 #define PIPEACONF 0x70008 #define PIPEBCONF 0x71008 +#define PIPEASTAT 0x70024 /* bits 0-15 are "write 1 to clear" */ +#define PIPEBSTAT 0x71024 + #define PIPECONF_ENABLE (1 << 31) #define PIPECONF_DISABLE 0 #define PIPECONF_DOUBLE_WIDE (1 << 30) @@ -293,6 +296,31 @@ #define PIPECONF_INTERLACE_FIELD_0_ONLY (7 << 21) #define PIPECONF_INTERLACE_MASK (7 << 21) +/* enable bits, write 1 to enable */ +#define PIPESTAT_FIFO_UNDERRUN (1 << 31) +#define PIPESTAT_CRC_ERROR_EN (1 << 29) +#define PIPESTAT_CRC_DONE_EN (1 << 28) +#define PIPESTAT_HOTPLUG_EN (1 << 26) +#define PIPESTAT_VERTICAL_SYNC_EN (1 << 25) +#define PIPESTAT_DISPLINE_COMP_EN (1 << 24) +#define PIPESTAT_FLD_EVT_ODD_EN (1 << 21) +#define PIPESTAT_FLD_EVT_EVEN_EN (1 << 20) +#define PIPESTAT_TV_HOTPLUG_EN (1 << 18) +#define PIPESTAT_VBLANK_EN (1 << 17) +#define PIPESTAT_OVL_UPDATE_EN (1 << 16) +/* status bits, write 1 to clear */ +#define PIPESTAT_HOTPLUG_STATE (1 << 15) +#define PIPESTAT_CRC_ERROR (1 << 13) +#define PIPESTAT_CRC_DONE (1 << 12) +#define PIPESTAT_HOTPLUG (1 << 10) +#define PIPESTAT_VSYNC (1 << 9) +#define PIPESTAT_DISPLINE_COMP (1 << 8) +#define PIPESTAT_FLD_EVT_ODD (1 << 5) +#define PIPESTAT_FLD_EVT_EVEN (1 << 4) +#define PIPESTAT_TV_HOTPLUG (1 << 2) +#define PIPESTAT_VBLANK (1 << 1) +#define PIPESTAT_OVL_UPDATE (1 << 0) + #define DISPARB 0x70030 #define DISPARB_AEND_MASK 0x1ff #define DISPARB_AEND_SHIFT 0 @@ -573,7 +601,7 @@ extern void intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg, extern void intelfbhw_cursor_load(struct intelfb_info *dinfo, int width, int height, u8 *data); extern void intelfbhw_cursor_reset(struct intelfb_info *dinfo); -extern int intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable); +extern int intelfbhw_enable_irq(struct intelfb_info *dinfo); extern void intelfbhw_disable_irq(struct intelfb_info *dinfo); extern int intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe); |