Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7649757b authored by Eric Hustvedt's avatar Eric Hustvedt Committed by Dave Airlie
Browse files

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: default avatarEric Hustvedt <ehustvedt@cecropia.com>
parent 9a5f019b
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -208,6 +208,11 @@ struct intelfb_heap_data {
	u32 size;    // in bytes
};

struct intelfb_vsync {
	wait_queue_head_t wait;
	unsigned int count;
};

struct intelfb_info {
	struct fb_info *info;
	struct fb_ops  *fbops;
@@ -271,6 +276,12 @@ struct intelfb_info {
	int fixed_mode;
	int ring_active;
	int flag;
	unsigned long irq_flags;
	int open;

	/* vsync */
	struct intelfb_vsync vsync;
	spinlock_t int_lock;

	/* hw cursor */
	int cursor_on;
+39 −0
Original line number Diff line number Diff line
@@ -137,6 +137,8 @@
static void __devinit get_initial_mode(struct intelfb_info *dinfo);
static void update_dinfo(struct intelfb_info *dinfo,
			 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,
			     struct fb_info *info);
static int intelfb_set_par(struct fb_info *info);
@@ -195,6 +197,8 @@ static int num_registered = 0;
/* fb ops */
static struct fb_ops intel_fb_ops = {
	.owner =		THIS_MODULE,
	.fb_open =              intelfb_open,
	.fb_release =           intelfb_release,
	.fb_check_var =         intelfb_check_var,
	.fb_set_par =           intelfb_set_par,
	.fb_setcolreg =		intelfb_setcolreg,
@@ -447,6 +451,8 @@ cleanup(struct intelfb_info *dinfo)
	if (!dinfo)
		return;

	intelfbhw_disable_irq(dinfo);

	fb_dealloc_cmap(&dinfo->info->cmap);
	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->open = 0;

	init_waitqueue_head(&dinfo->vsync.wait);
	spin_lock_init(&dinfo->int_lock);
	dinfo->irq_flags = 0;

	return 0;

@@ -1188,6 +1199,34 @@ update_dinfo(struct intelfb_info *dinfo, struct fb_var_screeninfo *var)
 *                       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
intelfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
+76 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include <linux/pagemap.h>
#include <linux/interrupt.h>

#include <asm/io.h>

@@ -1943,3 +1944,78 @@ intelfbhw_cursor_reset(struct intelfb_info *dinfo) {
		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);
	}
}
+2 −0
Original line number Diff line number Diff line
@@ -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,
				  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 void intelfbhw_disable_irq(struct intelfb_info *dinfo);

#endif /* _INTELFBHW_H */