diff options
author | Stefan Agner <stefan.agner@toradex.com> | 2015-07-02 08:34:27 +0200 |
---|---|---|
committer | Stefan Agner <stefan.agner@toradex.com> | 2015-07-02 09:27:05 +0200 |
commit | 9a0eb5848d988ee5e1c60f1d2495ffa657510131 (patch) | |
tree | 4791fbed7602ed656654854094d6552279b5d228 | |
parent | bb2725c66b7d48698f2fd663314b80aef44e00f4 (diff) |
video: fsl-dcu-fb: implement wait for VSYNC ioctrltoradex_vf_4.0-next
Implement ioctrl FBIO_WAITFORVSYNC which allows user space to wait for
next VSYNC. The ioctrl waits until the next vertical synchronization
phase has begun.
This reintroduces interrupt handling which has been removed in a
earlier commit 85f663301364 ("video: fsl-dcu-fb: remove underrun
interrupt handling")
-rw-r--r-- | drivers/video/fbdev/fsl-dcu-fb.c | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/drivers/video/fbdev/fsl-dcu-fb.c b/drivers/video/fbdev/fsl-dcu-fb.c index d43c62f80cc8..1487767426f4 100644 --- a/drivers/video/fbdev/fsl-dcu-fb.c +++ b/drivers/video/fbdev/fsl-dcu-fb.c @@ -11,6 +11,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/completion.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h> #include <linux/fb.h> @@ -68,9 +69,11 @@ #define DCU_INT_STATUS 0x002C #define DCU_INT_STATUS_UNDRUN (1 << 1) +#define DCU_INT_STATUS_VSYNC (1 << 0) #define DCU_INT_MASK 0x0030 #define DCU_INT_MASK_UNDRUN (1 << 1) +#define DCU_INT_MASK_VSYNC (1 << 0) #define DCU_DIV_RATIO 0x0054 @@ -146,6 +149,7 @@ struct dcu_fb_data { struct fb_videomode native_mode; u32 bits_per_pixel; bool pixclockpol; + struct completion vsync_wait; }; struct layer_display_offset { @@ -681,6 +685,30 @@ static int fsl_dcu_blank(int blank_mode, struct fb_info *info) return 0; } +static int fsl_dcu_wait_for_vsync(struct dcu_fb_data *dcufb) +{ + unsigned long mask = readl(dcufb->reg_base + DCU_INT_MASK); + int ret; + + /* + * Clear current VSYNC status since that still contains the flag from + * last VSYNC... + */ + writel(DCU_INT_STATUS_VSYNC, dcufb->reg_base + DCU_INT_STATUS); + writel(mask & ~DCU_INT_MASK_VSYNC, dcufb->reg_base + DCU_INT_MASK); + + ret = wait_for_completion_timeout(&dcufb->vsync_wait, HZ / 2); + + if (ret < 0) + return ret; + if (ret == 0) { + dev_warn(dcufb->dev, "wait_for_vsync timed out!\n"); + return -ETIMEDOUT; + } + + return 0; +} + static int fsl_dcu_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { @@ -716,6 +744,8 @@ static int fsl_dcu_ioctl(struct fb_info *info, unsigned int cmd, mfbi->alpha = alpha; fsl_dcu_set_par(info); break; + case FBIO_WAITFORVSYNC: + return fsl_dcu_wait_for_vsync(dcufb); default: dev_err(dcufb->dev, "unknown ioctl command (0x%08X)\n", cmd); return -ENOIOCTLCMD; @@ -948,6 +978,23 @@ static void uninstall_framebuffer(struct fb_info *info) fb_dealloc_cmap(&info->cmap); } +static irqreturn_t fsl_dcu_irq(int irq, void *dev_id) +{ + struct dcu_fb_data *dcufb = dev_id; + unsigned int mask = readl(dcufb->reg_base + DCU_INT_MASK); + unsigned int status = readl(dcufb->reg_base + DCU_INT_STATUS); + + if (status & DCU_INT_STATUS_VSYNC) { + mask |= DCU_INT_MASK_VSYNC; + writel(mask, dcufb->reg_base + DCU_INT_MASK); + complete(&dcufb->vsync_wait); + } + + writel(status, dcufb->reg_base + DCU_INT_STATUS); + + return IRQ_HANDLED; +} + #ifdef CONFIG_PM_RUNTIME static int fsl_dcu_runtime_suspend(struct device *dev) { @@ -1060,6 +1107,9 @@ static int fsl_dcu_probe(struct platform_device *pdev) if (!dcufb) return -ENOMEM; + /* initialize the vsync wait queue */ + init_completion(&dcufb->vsync_wait); + dcufb->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, dcufb); @@ -1081,6 +1131,13 @@ static int fsl_dcu_probe(struct platform_device *pdev) return -EINVAL; } + ret = devm_request_irq(&pdev->dev, dcufb->irq, fsl_dcu_irq, + 0, DRIVER_NAME, dcufb); + if (ret) { + dev_err(&pdev->dev, "could not request irq\n"); + goto failed_ioremap; + } + /* Put TCON in bypass mode, so the input signals from DCU are passed * through TCON unchanged */ ret = bypass_tcon(pdev->dev.of_node); @@ -1148,6 +1205,7 @@ failed_alloc_framebuffer: failed_getclock: failed_bypasstcon: free_irq(dcufb->irq, dcufb); +failed_ioremap: return ret; } |