Merge "msm: mdss: implement vsync ctrl and events" into msm-3.4
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index 1a1bb07..1f83b2b 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -186,7 +186,7 @@
/*
* alloc framebuffer info + par data
*/
- fbi = framebuffer_alloc(sizeof(struct msm_fb_data_type), &pdev->dev);
+ fbi = framebuffer_alloc(sizeof(struct msm_fb_data_type), NULL);
if (fbi == NULL) {
pr_err("can't allocate framebuffer info data!\n");
return -ENOMEM;
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 17281d5..a282c3a 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -107,6 +107,9 @@
MDSS_MDP_MAX_CSC
};
+struct mdss_mdp_ctl;
+typedef void (*mdp_vsync_handler_t)(struct mdss_mdp_ctl *, ktime_t);
+
struct mdss_mdp_ctl {
u32 num;
u32 ref_cnt;
@@ -138,6 +141,7 @@
int (*stop_fnc) (struct mdss_mdp_ctl *ctl);
int (*prepare_fnc) (struct mdss_mdp_ctl *ctl, void *arg);
int (*display_fnc) (struct mdss_mdp_ctl *ctl, void *arg);
+ int (*set_vsync_handler) (struct mdss_mdp_ctl *, mdp_vsync_handler_t);
void *priv_data;
};
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
index bc64d2e..6f92474 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_video.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
@@ -13,6 +13,8 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
+#include <linux/workqueue.h>
+
#include "mdss_fb.h"
#include "mdss_mdp.h"
@@ -44,6 +46,12 @@
u8 timegen_en;
struct completion pp_comp;
struct completion vsync_comp;
+
+ struct mutex vsync_lock;
+ struct work_struct vsync_work;
+ mdp_vsync_handler_t vsync_handler;
+ void *vsync_ptr;
+ ktime_t vsync_time;
};
struct mdss_mdp_video_ctx mdss_mdp_video_ctx_list[MAX_SESSIONS];
@@ -141,6 +149,47 @@
return 0;
}
+static void send_vsync_work(struct work_struct *work)
+{
+ struct mdss_mdp_video_ctx *ctx;
+
+ ctx = container_of(work, typeof(*ctx), vsync_work);
+ mutex_lock(&ctx->vsync_lock);
+ if (ctx->vsync_handler)
+ ctx->vsync_handler(ctx->vsync_ptr, ctx->vsync_time);
+ mutex_unlock(&ctx->vsync_lock);
+}
+
+static int mdss_mdp_video_set_vsync_handler(struct mdss_mdp_ctl *ctl,
+ mdp_vsync_handler_t vsync_handler)
+{
+ struct mdss_mdp_video_ctx *ctx;
+
+ ctx = (struct mdss_mdp_video_ctx *) ctl->priv_data;
+ if (!ctx) {
+ pr_err("invalid ctx for ctl=%d\n", ctl->num);
+ return -ENODEV;
+ }
+ if (mutex_lock_interruptible(&ctx->vsync_lock))
+ return -EINTR;
+
+ if (!ctx->timegen_en) {
+ ctx->vsync_time = ktime_get();
+ schedule_work(&ctx->vsync_work);
+ }
+
+ if (!ctx->vsync_handler && vsync_handler)
+ mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
+ else if (ctx->vsync_handler && !vsync_handler)
+ mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
+
+ ctx->vsync_handler = vsync_handler;
+ ctx->vsync_ptr = ctl;
+ mutex_unlock(&ctx->vsync_lock);
+
+ return 0;
+}
+
static int mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl)
{
struct mdss_mdp_video_ctx *ctx;
@@ -161,6 +210,11 @@
ctx->timegen_en = false;
}
+ mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num,
+ NULL, NULL);
+ mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num,
+ NULL, NULL);
+
memset(ctx, 0, sizeof(*ctx));
return 0;
@@ -190,10 +244,13 @@
pr_err("invalid ctx\n");
return;
}
+ ctx->vsync_time = ktime_get();
pr_debug("intr ctl=%d\n", ctx->ctl_num);
complete(&ctx->vsync_comp);
+ if (ctx->vsync_handler)
+ schedule_work(&ctx->vsync_work);
}
static int mdss_mdp_video_prepare(struct mdss_mdp_ctl *ctl, void *arg)
@@ -207,15 +264,10 @@
}
if (ctx->timegen_en) {
- u32 intr_type = MDSS_MDP_IRQ_PING_PONG_COMP;
-
pr_debug("waiting for ping pong %d done\n", ctx->pp_num);
- mdss_mdp_set_intr_callback(intr_type, ctx->pp_num,
- mdss_mdp_video_pp_intr_done, ctx);
- mdss_mdp_irq_enable(intr_type, ctx->pp_num);
-
+ mdss_mdp_irq_enable(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num);
wait_for_completion_interruptible(&ctx->pp_comp);
- mdss_mdp_irq_disable(intr_type, ctx->pp_num);
+ mdss_mdp_irq_disable(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num);
}
return 0;
@@ -224,7 +276,6 @@
static int mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
{
struct mdss_mdp_video_ctx *ctx;
- u32 intr_type = MDSS_MDP_IRQ_INTF_VSYNC;
pr_debug("kickoff ctl=%d\n", ctl->num);
@@ -233,9 +284,12 @@
pr_err("invalid ctx\n");
return -ENODEV;
}
- mdss_mdp_set_intr_callback(intr_type, ctl->intf_num,
- mdss_mdp_video_vsync_intr_done, ctx);
- mdss_mdp_irq_enable(intr_type, ctl->intf_num);
+ INIT_COMPLETION(ctx->vsync_comp);
+
+ if (mutex_lock_interruptible(&ctx->vsync_lock))
+ return -EINTR;
+ if (!ctx->vsync_handler)
+ mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
if (!ctx->timegen_en) {
int off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);
@@ -249,7 +303,9 @@
}
wait_for_completion_interruptible(&ctx->vsync_comp);
- mdss_mdp_irq_disable(intr_type, ctl->intf_num);
+ if (!ctx->vsync_handler)
+ mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
+ mutex_unlock(&ctx->vsync_lock);
return 0;
}
@@ -293,6 +349,13 @@
init_completion(&ctx->pp_comp);
init_completion(&ctx->vsync_comp);
+ INIT_WORK(&ctx->vsync_work, send_vsync_work);
+ mutex_init(&ctx->vsync_lock);
+ mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num,
+ mdss_mdp_video_vsync_intr_done, ctx);
+ mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_COMP, ctx->pp_num,
+ mdss_mdp_video_pp_intr_done, ctx);
+
itp.width = pinfo->xres + pinfo->lcdc.xres_pad;
itp.height = pinfo->yres + pinfo->lcdc.yres_pad;
itp.border_clr = pinfo->lcdc.border_clr;
@@ -316,6 +379,7 @@
ctl->stop_fnc = mdss_mdp_video_stop;
ctl->prepare_fnc = mdss_mdp_video_prepare;
ctl->display_fnc = mdss_mdp_video_display;
+ ctl->set_vsync_handler = mdss_mdp_video_set_vsync_handler;
return 0;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index d4d889b..1cb474d 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -615,6 +615,49 @@
mfd->kickoff_fnc(mfd->ctl);
}
+static void mdss_mdp_overlay_handle_vsync(struct mdss_mdp_ctl *ctl, ktime_t t)
+{
+ struct device *dev;
+ char buf[64];
+ char *envp[2];
+
+ if (!ctl || !ctl->mfd || !ctl->mfd->fbi) {
+ pr_warn("Invalid handle for vsync\n");
+ return;
+ }
+
+ dev = ctl->mfd->fbi->dev;
+
+ snprintf(buf, sizeof(buf), "VSYNC=%llu", ktime_to_ns(t));
+ envp[0] = buf;
+ envp[1] = NULL;
+ kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
+
+ pr_debug("sent vsync on ctl=%d ts=%llu\n", ctl->num, ktime_to_ns(t));
+}
+
+static int mdss_mdp_overlay_vsync_ctrl(struct msm_fb_data_type *mfd, int en)
+{
+ struct mdss_mdp_ctl *ctl = mfd->ctl;
+ int rc;
+
+ if (!ctl)
+ return -ENODEV;
+ if (!ctl->set_vsync_handler)
+ return -ENOTSUPP;
+
+ pr_debug("vsync en=%d\n", en);
+
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+ if (en)
+ rc = ctl->set_vsync_handler(ctl, mdss_mdp_overlay_handle_vsync);
+ else
+ rc = ctl->set_vsync_handler(ctl, NULL);
+ mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
+
+ return rc;
+}
+
static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd,
struct fb_cursor *cursor)
{
@@ -822,6 +865,16 @@
}
break;
+ case MSMFB_VSYNC_CTRL:
+ case MSMFB_OVERLAY_VSYNC_CTRL:
+ if (!copy_from_user(&val, argp, sizeof(val))) {
+ ret = mdss_mdp_overlay_vsync_ctrl(mfd, val);
+ } else {
+ pr_err("MSMFB_OVERLAY_VSYNC_CTRL failed (%d)\n", ret);
+ ret = -EFAULT;
+ }
+ break;
+
default:
if (mfd->panel_info.type == WRITEBACK_PANEL)
ret = mdss_mdp_wb_ioctl_handler(mfd, cmd, argp);
diff --git a/drivers/video/msm/mdss/mdss_mdp_util.c b/drivers/video/msm/mdss/mdss_mdp_util.c
index 7f61a14..7567db1 100644
--- a/drivers/video/msm/mdss/mdss_mdp_util.c
+++ b/drivers/video/msm/mdss/mdss_mdp_util.c
@@ -102,8 +102,6 @@
spin_lock(&mdss_mdp_intr_lock);
fnc = mdp_intr_cb[index].func;
arg = mdp_intr_cb[index].arg;
- if (fnc != NULL)
- mdp_intr_cb[index].func = NULL;
spin_unlock(&mdss_mdp_intr_lock);
if (fnc)
fnc(arg);