Skip to content

Commit

Permalink
s3c-fb: add wait for VSYNC ioctl
Browse files Browse the repository at this point in the history
Add VSYNC interrupt support and an ioctl that allows waiting for it.
Interrupts are turned on only when needed.

Signed-off-by: Pawel Osciak <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
Cc: InKi Dae <[email protected]>
Cc: Ben Dooks <[email protected]>
Cc: Marek Szyprowski <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Pawel Osciak authored and torvalds committed Aug 11, 2010
1 parent 067b226 commit efdc846
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 1 deletion.
1 change: 1 addition & 0 deletions arch/arm/plat-samsung/include/plat/regs-fb.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
166 changes: 165 additions & 1 deletion drivers/video/s3c-fb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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.
Expand All @@ -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;
Expand All @@ -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;
};

/**
Expand Down Expand Up @@ -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,
Expand All @@ -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,
};

/**
Expand Down Expand Up @@ -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) +
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -1127,14 +1286,17 @@ 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;
}
}

platform_set_drvdata(pdev, sfb);

return 0;

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

err_ioremap:
iounmap(sfb->regs);

Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit efdc846

Please sign in to comment.