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

Commit efdc846d authored by Pawel Osciak's avatar Pawel Osciak Committed by Linus Torvalds
Browse files

s3c-fb: add wait for VSYNC ioctl



Add VSYNC interrupt support and an ioctl that allows waiting for it.
Interrupts are turned on only when needed.

Signed-off-by: default avatarPawel Osciak <p.osciak@samsung.com>
Signed-off-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
Cc: InKi Dae <inki.dae@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 067b226b
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -300,6 +300,7 @@
#define VIDINTCON0_FRAMESEL0_FRONTPORCH		(0x3 << 15)

#define VIDINTCON0_FRAMESEL1			(1 << 13)
#define VIDINTCON0_FRAMESEL1_MASK		(0x3 << 13)
#define VIDINTCON0_FRAMESEL1_NONE		(0x0 << 13)
#define VIDINTCON0_FRAMESEL1_BACKPORCH		(0x1 << 13)
#define VIDINTCON0_FRAMESEL1_VSYNC		(0x2 << 13)
+165 −1
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@
#include <linux/clk.h>
#include <linux/fb.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>

#include <mach/map.h>
#include <plat/regs-fb-v4.h>
@@ -48,6 +50,11 @@
	__raw_writel(v, r); } while(0)
#endif /* FB_S3C_DEBUG_REGWRITE */

/* irq_flags bits */
#define S3C_FB_VSYNC_IRQ_EN	0

#define VSYNC_TIMEOUT_MSEC 50

struct s3c_fb;

#define VALID_BPP(x) (1 << ((x) - 1))
@@ -155,6 +162,16 @@ struct s3c_fb_win {
	unsigned int		 index;
};

/**
 * struct s3c_fb_vsync - vsync information
 * @wait:	a queue for processes waiting for vsync
 * @count:	vsync interrupt count
 */
struct s3c_fb_vsync {
	wait_queue_head_t	wait;
	unsigned int		count;
};

/**
 * struct s3c_fb - overall hardware state of the hardware
 * @dev: The device that we bound to, for printing, etc.
@@ -165,6 +182,9 @@ struct s3c_fb_win {
 * @enabled: A bitmask of enabled hardware windows.
 * @pdata: The platform configuration data passed with the device.
 * @windows: The hardware windows that have been claimed.
 * @irq_no: IRQ line number
 * @irq_flags: irq flags
 * @vsync_info: VSYNC-related information (count, queues...)
 */
struct s3c_fb {
	struct device		*dev;
@@ -177,6 +197,10 @@ struct s3c_fb {

	struct s3c_fb_platdata	*pdata;
	struct s3c_fb_win	*windows[S3C_FB_MAX_WIN];

	int			 irq_no;
	unsigned long		 irq_flags;
	struct s3c_fb_vsync	 vsync_info;
};

/**
@@ -798,6 +822,124 @@ static int s3c_fb_pan_display(struct fb_var_screeninfo *var,
	return 0;
}

/**
 * s3c_fb_enable_irq() - enable framebuffer interrupts
 * @sfb: main hardware state
 */
static void s3c_fb_enable_irq(struct s3c_fb *sfb)
{
	void __iomem *regs = sfb->regs;
	u32 irq_ctrl_reg;

	if (!test_and_set_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) {
		/* IRQ disabled, enable it */
		irq_ctrl_reg = readl(regs + VIDINTCON0);

		irq_ctrl_reg |= VIDINTCON0_INT_ENABLE;
		irq_ctrl_reg |= VIDINTCON0_INT_FRAME;

		irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL0_MASK;
		irq_ctrl_reg |= VIDINTCON0_FRAMESEL0_VSYNC;
		irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL1_MASK;
		irq_ctrl_reg |= VIDINTCON0_FRAMESEL1_NONE;

		writel(irq_ctrl_reg, regs + VIDINTCON0);
	}
}

/**
 * s3c_fb_disable_irq() - disable framebuffer interrupts
 * @sfb: main hardware state
 */
static void s3c_fb_disable_irq(struct s3c_fb *sfb)
{
	void __iomem *regs = sfb->regs;
	u32 irq_ctrl_reg;

	if (test_and_clear_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) {
		/* IRQ enabled, disable it */
		irq_ctrl_reg = readl(regs + VIDINTCON0);

		irq_ctrl_reg &= ~VIDINTCON0_INT_FRAME;
		irq_ctrl_reg &= ~VIDINTCON0_INT_ENABLE;

		writel(irq_ctrl_reg, regs + VIDINTCON0);
	}
}

static irqreturn_t s3c_fb_irq(int irq, void *dev_id)
{
	struct s3c_fb *sfb = dev_id;
	void __iomem  *regs = sfb->regs;
	u32 irq_sts_reg;

	irq_sts_reg = readl(regs + VIDINTCON1);

	if (irq_sts_reg & VIDINTCON1_INT_FRAME) {

		/* VSYNC interrupt, accept it */
		writel(VIDINTCON1_INT_FRAME, regs + VIDINTCON1);

		sfb->vsync_info.count++;
		wake_up_interruptible(&sfb->vsync_info.wait);
	}

	/* We only support waiting for VSYNC for now, so it's safe
	 * to always disable irqs here.
	 */
	s3c_fb_disable_irq(sfb);

	return IRQ_HANDLED;
}

/**
 * s3c_fb_wait_for_vsync() - sleep until next VSYNC interrupt or timeout
 * @sfb: main hardware state
 * @crtc: head index.
 */
static int s3c_fb_wait_for_vsync(struct s3c_fb *sfb, u32 crtc)
{
	unsigned long count;
	int ret;

	if (crtc != 0)
		return -ENODEV;

	count = sfb->vsync_info.count;
	s3c_fb_enable_irq(sfb);
	ret = wait_event_interruptible_timeout(sfb->vsync_info.wait,
				       count != sfb->vsync_info.count,
				       msecs_to_jiffies(VSYNC_TIMEOUT_MSEC));
	if (ret == 0)
		return -ETIMEDOUT;

	return 0;
}

static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd,
			unsigned long arg)
{
	struct s3c_fb_win *win = info->par;
	struct s3c_fb *sfb = win->parent;
	int ret;
	u32 crtc;

	switch (cmd) {
	case FBIO_WAITFORVSYNC:
		if (get_user(crtc, (u32 __user *)arg)) {
			ret = -EFAULT;
			break;
		}

		ret = s3c_fb_wait_for_vsync(sfb, crtc);
		break;
	default:
		ret = -ENOTTY;
	}

	return ret;
}

static struct fb_ops s3c_fb_ops = {
	.owner		= THIS_MODULE,
	.fb_check_var	= s3c_fb_check_var,
@@ -808,6 +950,7 @@ static struct fb_ops s3c_fb_ops = {
	.fb_copyarea	= cfb_copyarea,
	.fb_imageblit	= cfb_imageblit,
	.fb_pan_display	= s3c_fb_pan_display,
	.fb_ioctl	= s3c_fb_ioctl,
};

/**
@@ -914,6 +1057,8 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,

	dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant);

	init_waitqueue_head(&sfb->vsync_info.wait);

	palette_size = variant->palette_sz * 4;

	fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +
@@ -1093,6 +1238,20 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
		goto err_req_region;
	}

	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	if (!res) {
		dev_err(dev, "failed to acquire irq resource\n");
		ret = -ENOENT;
		goto err_ioremap;
	}
	sfb->irq_no = res->start;
	ret = request_irq(sfb->irq_no, s3c_fb_irq,
			  0, "s3c_fb", sfb);
	if (ret) {
		dev_err(dev, "irq request failed\n");
		goto err_ioremap;
	}

	dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs);

	/* setup gpio and output polarity controls */
@@ -1127,7 +1286,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
			dev_err(dev, "failed to create window %d\n", win);
			for (; win >= 0; win--)
				s3c_fb_release_win(sfb, sfb->windows[win]);
			goto err_ioremap;
			goto err_irq;
		}
	}

@@ -1135,6 +1294,9 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)

	return 0;

err_irq:
	free_irq(sfb->irq_no, sfb);

err_ioremap:
	iounmap(sfb->regs);

@@ -1167,6 +1329,8 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev)
		if (sfb->windows[win])
			s3c_fb_release_win(sfb, sfb->windows[win]);

	free_irq(sfb->irq_no, sfb);

	iounmap(sfb->regs);

	clk_disable(sfb->bus_clk);