aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
authorDaniel Hillenbrand <daniel.hillenbrand@codeworkx.de>2012-07-27 02:03:16 +0200
committerDaniel Hillenbrand <daniel.hillenbrand@codeworkx.de>2012-07-27 02:20:26 +0200
commite4ab2a76603300c573de592a00aed5096533b032 (patch)
treeebd839e7eb8d046f31af4ba2e1e48eb164ac1963 /drivers/video
parent5eb00c0353bc1987b1e40759c172dd2a839a4707 (diff)
downloadkernel_samsung_smdk4412-e4ab2a76603300c573de592a00aed5096533b032.zip
kernel_samsung_smdk4412-e4ab2a76603300c573de592a00aed5096533b032.tar.gz
kernel_samsung_smdk4412-e4ab2a76603300c573de592a00aed5096533b032.tar.bz2
s3cfb: asynchronous vsync notification
Change-Id: Id3642cd25475b3c872b1ae7d4b69d53f122dffa8
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/samsung/s3cfb.h4
-rw-r--r--drivers/video/samsung/s3cfb_main.c41
-rw-r--r--drivers/video/samsung/s3cfb_ops.c21
3 files changed, 63 insertions, 3 deletions
diff --git a/drivers/video/samsung/s3cfb.h b/drivers/video/samsung/s3cfb.h
index 6adb765..b278184 100644
--- a/drivers/video/samsung/s3cfb.h
+++ b/drivers/video/samsung/s3cfb.h
@@ -178,6 +178,10 @@ struct s3cfb_global {
unsigned int wq_count;
struct fb_info **fb;
+ ktime_t vsync_timestamp;
+ int vsync_state;
+ struct task_struct *vsync_thread;
+
atomic_t enabled_win;
enum s3cfb_output_t output;
enum s3cfb_rgb_mode_t rgb_mode;
diff --git a/drivers/video/samsung/s3cfb_main.c b/drivers/video/samsung/s3cfb_main.c
index a6e4564..2cb7c86 100644
--- a/drivers/video/samsung/s3cfb_main.c
+++ b/drivers/video/samsung/s3cfb_main.c
@@ -29,6 +29,7 @@
#include <linux/memory.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
+#include <linux/kthread.h>
#include <plat/clock.h>
#include <plat/media.h>
#include <mach/media.h>
@@ -57,6 +58,8 @@
#include <mach/regs-pmu.h>
#include <plat/regs-fb-s5p.h>
+extern int s3cfb_vsync_timestamp_changed(struct s3cfb_global *fbdev, ktime_t prev_timestamp);
+
struct s3cfb_fimd_desc *fbfimd;
inline struct s3cfb_global *get_fimd_global(int id)
@@ -91,7 +94,8 @@ static irqreturn_t s3cfb_irq_frame(int irq, void *dev_id)
s3cfb_clear_interrupt(fbdev[0]);
fbdev[0]->wq_count++;
- wake_up(&fbdev[0]->wq);
+ fbdev[0]->vsync_timestamp = ktime_get();
+ wake_up_interruptible(&fbdev[0]->wq);
return IRQ_HANDLED;
}
@@ -318,6 +322,32 @@ void s3cfb_trigger(void)
EXPORT_SYMBOL(s3cfb_trigger);
#endif
+static int s3cfb_wait_for_vsync_thread(void *data)
+{
+ struct s3cfb_global *fbdev = data;
+
+ while (!kthread_should_stop()) {
+ ktime_t prev_timestamp = fbdev->vsync_timestamp;
+
+ int ret = wait_event_interruptible_timeout(fbdev->wq,
+ s3cfb_vsync_timestamp_changed(fbdev, prev_timestamp),
+ msecs_to_jiffies(100));
+
+ if (ret > 0) {
+ char *envp[2];
+ char buf[64];
+
+ snprintf(buf, sizeof(buf), "VSYNC=%llu",
+ ktime_to_ns(fbdev->vsync_timestamp));
+ envp[0] = buf;
+ envp[1] = NULL;
+ kobject_uevent_env(&fbdev->dev->kobj, KOBJ_CHANGE, envp);
+ }
+ }
+
+ return 0;
+}
+
static int s3cfb_probe(struct platform_device *pdev)
{
struct s3c_platform_fb *pdata = NULL;
@@ -494,6 +524,12 @@ static int s3cfb_probe(struct platform_device *pdev)
pdata->lcd_on(pdev);
#endif
+ fbdev[0]->vsync_thread = kthread_run(s3cfb_wait_for_vsync_thread, fbdev[0], "s3cfb-vsync");
+ if (fbdev[0]->vsync_thread == ERR_PTR(-ENOMEM)) {
+ dev_err(fbdev[0]->dev, "failed to run vsync thread\n");
+ fbdev[0]->vsync_thread = NULL;
+ }
+
ret = device_create_file(&(pdev->dev), &dev_attr_win_power);
if (ret < 0)
dev_err(fbdev[0]->dev, "failed to add sysfs entries\n");
@@ -565,6 +601,9 @@ static int s3cfb_remove(struct platform_device *pdev)
}
}
+ if (fbdev[i]->vsync_thread)
+ kthread_stop(fbdev[i]->vsync_thread);
+
kfree(fbdev[i]->fb);
kfree(fbdev[i]);
}
diff --git a/drivers/video/samsung/s3cfb_ops.c b/drivers/video/samsung/s3cfb_ops.c
index 3ff2f05..82ece16 100644
--- a/drivers/video/samsung/s3cfb_ops.c
+++ b/drivers/video/samsung/s3cfb_ops.c
@@ -1082,15 +1082,32 @@ int s3cfb_cursor(struct fb_info *fb, struct fb_cursor *cursor)
return 0;
}
+int s3cfb_vsync_timestamp_changed(struct s3cfb_global *fbdev, ktime_t prev_timestamp)
+{
+ return !ktime_equal(prev_timestamp, fbdev->vsync_timestamp);
+}
+
int s3cfb_wait_for_vsync(struct s3cfb_global *fbdev)
{
+ ktime_t prev_timestamp;
+ int ret;
+
dev_dbg(fbdev->dev, "waiting for VSYNC interrupt\n");
- sleep_on_timeout(&fbdev->wq, HZ / 10);
+ prev_timestamp = fbdev->vsync_timestamp;
+
+ ret = wait_event_interruptible_timeout(fbdev->wq,
+ s3cfb_vsync_timestamp_changed(fbdev, prev_timestamp),
+ msecs_to_jiffies(100));
+
+ if (ret == 0)
+ return -ETIMEDOUT;
+ if (ret < 0)
+ return ret;
dev_dbg(fbdev->dev, "got a VSYNC interrupt\n");
- return 0;
+ return ret;
}
int s3cfb_ioctl(struct fb_info *fb, unsigned int cmd, unsigned long arg)