mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-10 15:10:38 +00:00
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>
This commit is contained in:
parent
9a5f019b1a
commit
7649757bd9
@ -208,6 +208,11 @@ struct intelfb_heap_data {
|
|||||||
u32 size; // in bytes
|
u32 size; // in bytes
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct intelfb_vsync {
|
||||||
|
wait_queue_head_t wait;
|
||||||
|
unsigned int count;
|
||||||
|
};
|
||||||
|
|
||||||
struct intelfb_info {
|
struct intelfb_info {
|
||||||
struct fb_info *info;
|
struct fb_info *info;
|
||||||
struct fb_ops *fbops;
|
struct fb_ops *fbops;
|
||||||
@ -271,6 +276,12 @@ struct intelfb_info {
|
|||||||
int fixed_mode;
|
int fixed_mode;
|
||||||
int ring_active;
|
int ring_active;
|
||||||
int flag;
|
int flag;
|
||||||
|
unsigned long irq_flags;
|
||||||
|
int open;
|
||||||
|
|
||||||
|
/* vsync */
|
||||||
|
struct intelfb_vsync vsync;
|
||||||
|
spinlock_t int_lock;
|
||||||
|
|
||||||
/* hw cursor */
|
/* hw cursor */
|
||||||
int cursor_on;
|
int cursor_on;
|
||||||
|
@ -137,6 +137,8 @@
|
|||||||
static void __devinit get_initial_mode(struct intelfb_info *dinfo);
|
static void __devinit get_initial_mode(struct intelfb_info *dinfo);
|
||||||
static void update_dinfo(struct intelfb_info *dinfo,
|
static void update_dinfo(struct intelfb_info *dinfo,
|
||||||
struct fb_var_screeninfo *var);
|
struct fb_var_screeninfo *var);
|
||||||
|
static int intelfb_open(struct fb_info *info, int user);
|
||||||
|
static int intelfb_release(struct fb_info *info, int user);
|
||||||
static int intelfb_check_var(struct fb_var_screeninfo *var,
|
static int intelfb_check_var(struct fb_var_screeninfo *var,
|
||||||
struct fb_info *info);
|
struct fb_info *info);
|
||||||
static int intelfb_set_par(struct fb_info *info);
|
static int intelfb_set_par(struct fb_info *info);
|
||||||
@ -195,6 +197,8 @@ static int num_registered = 0;
|
|||||||
/* fb ops */
|
/* fb ops */
|
||||||
static struct fb_ops intel_fb_ops = {
|
static struct fb_ops intel_fb_ops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
.fb_open = intelfb_open,
|
||||||
|
.fb_release = intelfb_release,
|
||||||
.fb_check_var = intelfb_check_var,
|
.fb_check_var = intelfb_check_var,
|
||||||
.fb_set_par = intelfb_set_par,
|
.fb_set_par = intelfb_set_par,
|
||||||
.fb_setcolreg = intelfb_setcolreg,
|
.fb_setcolreg = intelfb_setcolreg,
|
||||||
@ -447,6 +451,8 @@ cleanup(struct intelfb_info *dinfo)
|
|||||||
if (!dinfo)
|
if (!dinfo)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
intelfbhw_disable_irq(dinfo);
|
||||||
|
|
||||||
fb_dealloc_cmap(&dinfo->info->cmap);
|
fb_dealloc_cmap(&dinfo->info->cmap);
|
||||||
kfree(dinfo->info->pixmap.addr);
|
kfree(dinfo->info->pixmap.addr);
|
||||||
|
|
||||||
@ -889,6 +895,11 @@ intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
dinfo->registered = 1;
|
dinfo->registered = 1;
|
||||||
|
dinfo->open = 0;
|
||||||
|
|
||||||
|
init_waitqueue_head(&dinfo->vsync.wait);
|
||||||
|
spin_lock_init(&dinfo->int_lock);
|
||||||
|
dinfo->irq_flags = 0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -1188,6 +1199,34 @@ update_dinfo(struct intelfb_info *dinfo, struct fb_var_screeninfo *var)
|
|||||||
* fbdev interface *
|
* fbdev interface *
|
||||||
***************************************************************/
|
***************************************************************/
|
||||||
|
|
||||||
|
static int
|
||||||
|
intelfb_open(struct fb_info *info, int user)
|
||||||
|
{
|
||||||
|
struct intelfb_info *dinfo = GET_DINFO(info);
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
dinfo->open++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
intelfb_release(struct fb_info *info, int user)
|
||||||
|
{
|
||||||
|
struct intelfb_info *dinfo = GET_DINFO(info);
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
dinfo->open--;
|
||||||
|
msleep(1);
|
||||||
|
if (!dinfo->open) {
|
||||||
|
intelfbhw_disable_irq(dinfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
intelfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
|
intelfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
|
||||||
{
|
{
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
#include <linux/pagemap.h>
|
#include <linux/pagemap.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
@ -1943,3 +1944,78 @@ intelfbhw_cursor_reset(struct intelfb_info *dinfo) {
|
|||||||
addr += 16;
|
addr += 16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static irqreturn_t
|
||||||
|
intelfbhw_irq(int irq, void *dev_id, struct pt_regs *fp) {
|
||||||
|
int handled = 0;
|
||||||
|
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 (tmp == 0) {
|
||||||
|
spin_unlock(&dinfo->int_lock);
|
||||||
|
return IRQ_RETVAL(handled);
|
||||||
|
}
|
||||||
|
|
||||||
|
OUTREG16(IIR, tmp);
|
||||||
|
|
||||||
|
if (tmp & VSYNC_PIPE_A_INTERRUPT) {
|
||||||
|
dinfo->vsync.count++;
|
||||||
|
wake_up_interruptible(&dinfo->vsync.wait);
|
||||||
|
handled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock(&dinfo->int_lock);
|
||||||
|
|
||||||
|
return IRQ_RETVAL(handled);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable) {
|
||||||
|
|
||||||
|
if (!test_and_set_bit(0, &dinfo->irq_flags)) {
|
||||||
|
if (request_irq(dinfo->pdev->irq, intelfbhw_irq, SA_SHIRQ, "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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
intelfbhw_disable_irq(struct intelfb_info *dinfo) {
|
||||||
|
u16 tmp;
|
||||||
|
|
||||||
|
if (test_and_clear_bit(0, &dinfo->irq_flags)) {
|
||||||
|
spin_lock_irq(&dinfo->int_lock);
|
||||||
|
OUTREG16(HWSTAM, 0xffff);
|
||||||
|
OUTREG16(IMR, 0xffff);
|
||||||
|
OUTREG16(IER, 0x0);
|
||||||
|
|
||||||
|
tmp = INREG16(IIR);
|
||||||
|
OUTREG16(IIR, tmp);
|
||||||
|
spin_unlock_irq(&dinfo->int_lock);
|
||||||
|
|
||||||
|
free_irq(dinfo->pdev->irq, dinfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -561,5 +561,7 @@ extern void intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg,
|
|||||||
extern void intelfbhw_cursor_load(struct intelfb_info *dinfo, int width,
|
extern void intelfbhw_cursor_load(struct intelfb_info *dinfo, int width,
|
||||||
int height, u8 *data);
|
int height, u8 *data);
|
||||||
extern void intelfbhw_cursor_reset(struct intelfb_info *dinfo);
|
extern void intelfbhw_cursor_reset(struct intelfb_info *dinfo);
|
||||||
|
extern int intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable);
|
||||||
|
extern void intelfbhw_disable_irq(struct intelfb_info *dinfo);
|
||||||
|
|
||||||
#endif /* _INTELFBHW_H */
|
#endif /* _INTELFBHW_H */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user