diff options
| author | Eric Hustvedt <ehustvedt@cecropia.com> | 2006-06-20 14:36:41 -0400 |
|---|---|---|
| committer | Dave Airlie <airlied@linux.ie> | 2006-07-03 04:59:46 -0400 |
| commit | 7649757bd900bc900adcd95ab08903cdc28342fa (patch) | |
| tree | 4c710d9e458ff3c6731180aca738123886f7adec /drivers/video/intelfb | |
| parent | 9a5f019b1a9ea6a75ba36d7c312ff069006ed479 (diff) | |
intelfb: add vsync interrupt support
[03/05] intelfb: Implement basic interrupt handling
Functions have been added to enable and disable interrupts using the MMIO registers. Currently only pipe A vsync interrupts are enabled.
A generalized vsync accounting struct is defined, with the intent that it can encapsulate per-pipe vsync related info in the future. Currently a single instance is hard-coded.
The interrupt service routine currently only looks for vsync interrupts on pipe A, and increments a counter and wakes up anyone waiting on it.
This implementation is heavily influenced by similar implementations in the atyfb and matroxfb drivers.
Signed-off-by: Eric Hustvedt <ehustvedt@cecropia.com>
Diffstat (limited to 'drivers/video/intelfb')
| -rw-r--r-- | drivers/video/intelfb/intelfb.h | 11 | ||||
| -rw-r--r-- | drivers/video/intelfb/intelfbdrv.c | 39 | ||||
| -rw-r--r-- | drivers/video/intelfb/intelfbhw.c | 76 | ||||
| -rw-r--r-- | drivers/video/intelfb/intelfbhw.h | 2 |
4 files changed, 128 insertions, 0 deletions
diff --git a/drivers/video/intelfb/intelfb.h b/drivers/video/intelfb/intelfb.h index cb016fe4d488..dab1f2d764d2 100644 --- a/drivers/video/intelfb/intelfb.h +++ b/drivers/video/intelfb/intelfb.h | |||
| @@ -208,6 +208,11 @@ struct intelfb_heap_data { | |||
| 208 | u32 size; // in bytes | 208 | u32 size; // in bytes |
| 209 | }; | 209 | }; |
| 210 | 210 | ||
| 211 | struct intelfb_vsync { | ||
| 212 | wait_queue_head_t wait; | ||
| 213 | unsigned int count; | ||
| 214 | }; | ||
| 215 | |||
| 211 | struct intelfb_info { | 216 | struct intelfb_info { |
| 212 | struct fb_info *info; | 217 | struct fb_info *info; |
| 213 | struct fb_ops *fbops; | 218 | struct fb_ops *fbops; |
| @@ -271,6 +276,12 @@ struct intelfb_info { | |||
| 271 | int fixed_mode; | 276 | int fixed_mode; |
| 272 | int ring_active; | 277 | int ring_active; |
| 273 | int flag; | 278 | int flag; |
| 279 | unsigned long irq_flags; | ||
| 280 | int open; | ||
| 281 | |||
| 282 | /* vsync */ | ||
| 283 | struct intelfb_vsync vsync; | ||
| 284 | spinlock_t int_lock; | ||
| 274 | 285 | ||
| 275 | /* hw cursor */ | 286 | /* hw cursor */ |
| 276 | int cursor_on; | 287 | int cursor_on; |
diff --git a/drivers/video/intelfb/intelfbdrv.c b/drivers/video/intelfb/intelfbdrv.c index 0a0a8b199ecc..068c56d4e652 100644 --- a/drivers/video/intelfb/intelfbdrv.c +++ b/drivers/video/intelfb/intelfbdrv.c | |||
| @@ -137,6 +137,8 @@ | |||
| 137 | static void __devinit get_initial_mode(struct intelfb_info *dinfo); | 137 | static void __devinit get_initial_mode(struct intelfb_info *dinfo); |
| 138 | static void update_dinfo(struct intelfb_info *dinfo, | 138 | static void update_dinfo(struct intelfb_info *dinfo, |
| 139 | struct fb_var_screeninfo *var); | 139 | struct fb_var_screeninfo *var); |
| 140 | static int intelfb_open(struct fb_info *info, int user); | ||
| 141 | static int intelfb_release(struct fb_info *info, int user); | ||
| 140 | static int intelfb_check_var(struct fb_var_screeninfo *var, | 142 | static int intelfb_check_var(struct fb_var_screeninfo *var, |
| 141 | struct fb_info *info); | 143 | struct fb_info *info); |
| 142 | static int intelfb_set_par(struct fb_info *info); | 144 | static int intelfb_set_par(struct fb_info *info); |
| @@ -195,6 +197,8 @@ static int num_registered = 0; | |||
| 195 | /* fb ops */ | 197 | /* fb ops */ |
| 196 | static struct fb_ops intel_fb_ops = { | 198 | static struct fb_ops intel_fb_ops = { |
| 197 | .owner = THIS_MODULE, | 199 | .owner = THIS_MODULE, |
| 200 | .fb_open = intelfb_open, | ||
| 201 | .fb_release = intelfb_release, | ||
| 198 | .fb_check_var = intelfb_check_var, | 202 | .fb_check_var = intelfb_check_var, |
| 199 | .fb_set_par = intelfb_set_par, | 203 | .fb_set_par = intelfb_set_par, |
| 200 | .fb_setcolreg = intelfb_setcolreg, | 204 | .fb_setcolreg = intelfb_setcolreg, |
| @@ -447,6 +451,8 @@ cleanup(struct intelfb_info *dinfo) | |||
| 447 | if (!dinfo) | 451 | if (!dinfo) |
| 448 | return; | 452 | return; |
| 449 | 453 | ||
| 454 | intelfbhw_disable_irq(dinfo); | ||
| 455 | |||
| 450 | fb_dealloc_cmap(&dinfo->info->cmap); | 456 | fb_dealloc_cmap(&dinfo->info->cmap); |
| 451 | kfree(dinfo->info->pixmap.addr); | 457 | kfree(dinfo->info->pixmap.addr); |
| 452 | 458 | ||
| @@ -889,6 +895,11 @@ intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
| 889 | } | 895 | } |
| 890 | 896 | ||
| 891 | dinfo->registered = 1; | 897 | dinfo->registered = 1; |
| 898 | dinfo->open = 0; | ||
| 899 | |||
| 900 | init_waitqueue_head(&dinfo->vsync.wait); | ||
| 901 | spin_lock_init(&dinfo->int_lock); | ||
| 902 | dinfo->irq_flags = 0; | ||
| 892 | 903 | ||
| 893 | return 0; | 904 | return 0; |
| 894 | 905 | ||
| @@ -1189,6 +1200,34 @@ update_dinfo(struct intelfb_info *dinfo, struct fb_var_screeninfo *var) | |||
| 1189 | ***************************************************************/ | 1200 | ***************************************************************/ |
| 1190 | 1201 | ||
| 1191 | static int | 1202 | static int |
| 1203 | intelfb_open(struct fb_info *info, int user) | ||
| 1204 | { | ||
| 1205 | struct intelfb_info *dinfo = GET_DINFO(info); | ||
| 1206 | |||
| 1207 | if (user) { | ||
| 1208 | dinfo->open++; | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | return 0; | ||
| 1212 | } | ||
| 1213 | |||
| 1214 | static int | ||
| 1215 | intelfb_release(struct fb_info *info, int user) | ||
| 1216 | { | ||
| 1217 | struct intelfb_info *dinfo = GET_DINFO(info); | ||
| 1218 | |||
| 1219 | if (user) { | ||
| 1220 | dinfo->open--; | ||
| 1221 | msleep(1); | ||
| 1222 | if (!dinfo->open) { | ||
| 1223 | intelfbhw_disable_irq(dinfo); | ||
| 1224 | } | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | return 0; | ||
| 1228 | } | ||
| 1229 | |||
| 1230 | static int | ||
| 1192 | intelfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | 1231 | intelfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) |
| 1193 | { | 1232 | { |
| 1194 | int change_var = 0; | 1233 | int change_var = 0; |
diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c index 05aded669cdb..1a698a7230e0 100644 --- a/drivers/video/intelfb/intelfbhw.c +++ b/drivers/video/intelfb/intelfbhw.c | |||
| @@ -34,6 +34,7 @@ | |||
| 34 | #include <linux/pci.h> | 34 | #include <linux/pci.h> |
| 35 | #include <linux/vmalloc.h> | 35 | #include <linux/vmalloc.h> |
| 36 | #include <linux/pagemap.h> | 36 | #include <linux/pagemap.h> |
| 37 | #include <linux/interrupt.h> | ||
| 37 | 38 | ||
| 38 | #include <asm/io.h> | 39 | #include <asm/io.h> |
| 39 | 40 | ||
| @@ -1943,3 +1944,78 @@ intelfbhw_cursor_reset(struct intelfb_info *dinfo) { | |||
| 1943 | addr += 16; | 1944 | addr += 16; |
| 1944 | } | 1945 | } |
| 1945 | } | 1946 | } |
| 1947 | |||
| 1948 | static irqreturn_t | ||
| 1949 | intelfbhw_irq(int irq, void *dev_id, struct pt_regs *fp) { | ||
| 1950 | int handled = 0; | ||
| 1951 | u16 tmp; | ||
| 1952 | struct intelfb_info *dinfo = (struct intelfb_info *)dev_id; | ||
| 1953 | |||
| 1954 | spin_lock(&dinfo->int_lock); | ||
| 1955 | |||
| 1956 | tmp = INREG16(IIR); | ||
| 1957 | tmp &= VSYNC_PIPE_A_INTERRUPT; | ||
| 1958 | |||
| 1959 | if (tmp == 0) { | ||
| 1960 | spin_unlock(&dinfo->int_lock); | ||
| 1961 | return IRQ_RETVAL(handled); | ||
| 1962 | } | ||
| 1963 | |||
| 1964 | OUTREG16(IIR, tmp); | ||
| 1965 | |||
| 1966 | if (tmp & VSYNC_PIPE_A_INTERRUPT) { | ||
| 1967 | dinfo->vsync.count++; | ||
| 1968 | wake_up_interruptible(&dinfo->vsync.wait); | ||
| 1969 | handled = 1; | ||
| 1970 | } | ||
| 1971 | |||
| 1972 | spin_unlock(&dinfo->int_lock); | ||
| 1973 | |||
| 1974 | return IRQ_RETVAL(handled); | ||
| 1975 | } | ||
| 1976 | |||
| 1977 | int | ||
| 1978 | intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable) { | ||
| 1979 | |||
| 1980 | if (!test_and_set_bit(0, &dinfo->irq_flags)) { | ||
| 1981 | if (request_irq(dinfo->pdev->irq, intelfbhw_irq, SA_SHIRQ, "intelfb", dinfo)) { | ||
| 1982 | clear_bit(0, &dinfo->irq_flags); | ||
| 1983 | return -EINVAL; | ||
| 1984 | } | ||
| 1985 | |||
| 1986 | spin_lock_irq(&dinfo->int_lock); | ||
| 1987 | OUTREG16(HWSTAM, 0xfffe); | ||
| 1988 | OUTREG16(IMR, 0x0); | ||
| 1989 | OUTREG16(IER, VSYNC_PIPE_A_INTERRUPT); | ||
| 1990 | spin_unlock_irq(&dinfo->int_lock); | ||
| 1991 | } else if (reenable) { | ||
| 1992 | u16 ier; | ||
| 1993 | |||
| 1994 | spin_lock_irq(&dinfo->int_lock); | ||
| 1995 | ier = INREG16(IER); | ||
| 1996 | if ((ier & VSYNC_PIPE_A_INTERRUPT)) { | ||
| 1997 | DBG_MSG("someone disabled the IRQ [%08X]\n", ier); | ||
| 1998 | OUTREG(IER, VSYNC_PIPE_A_INTERRUPT); | ||
| 1999 | } | ||
| 2000 | spin_unlock_irq(&dinfo->int_lock); | ||
| 2001 | } | ||
| 2002 | return 0; | ||
| 2003 | } | ||
| 2004 | |||
| 2005 | void | ||
| 2006 | intelfbhw_disable_irq(struct intelfb_info *dinfo) { | ||
| 2007 | u16 tmp; | ||
| 2008 | |||
| 2009 | if (test_and_clear_bit(0, &dinfo->irq_flags)) { | ||
| 2010 | spin_lock_irq(&dinfo->int_lock); | ||
| 2011 | OUTREG16(HWSTAM, 0xffff); | ||
| 2012 | OUTREG16(IMR, 0xffff); | ||
| 2013 | OUTREG16(IER, 0x0); | ||
| 2014 | |||
| 2015 | tmp = INREG16(IIR); | ||
| 2016 | OUTREG16(IIR, tmp); | ||
| 2017 | spin_unlock_irq(&dinfo->int_lock); | ||
| 2018 | |||
| 2019 | free_irq(dinfo->pdev->irq, dinfo); | ||
| 2020 | } | ||
| 2021 | } | ||
diff --git a/drivers/video/intelfb/intelfbhw.h b/drivers/video/intelfb/intelfbhw.h index 8d2e36972fc8..aa0c139a2301 100644 --- a/drivers/video/intelfb/intelfbhw.h +++ b/drivers/video/intelfb/intelfbhw.h | |||
| @@ -561,5 +561,7 @@ extern void intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg, | |||
| 561 | extern void intelfbhw_cursor_load(struct intelfb_info *dinfo, int width, | 561 | extern void intelfbhw_cursor_load(struct intelfb_info *dinfo, int width, |
| 562 | int height, u8 *data); | 562 | int height, u8 *data); |
| 563 | extern void intelfbhw_cursor_reset(struct intelfb_info *dinfo); | 563 | extern void intelfbhw_cursor_reset(struct intelfb_info *dinfo); |
| 564 | extern int intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable); | ||
| 565 | extern void intelfbhw_disable_irq(struct intelfb_info *dinfo); | ||
| 564 | 566 | ||
| 565 | #endif /* _INTELFBHW_H */ | 567 | #endif /* _INTELFBHW_H */ |
