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 */ |