diff options
| author | Adrian Salido-Moreno <adrianm@codeaurora.org> | 2014-01-10 15:39:49 -0800 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 20:25:34 -0700 |
| commit | 7593d245112a12c991fd09cd2cd73afc70c5a6eb (patch) | |
| tree | 7a0b04c1e265f700bf6f97e4e9deb5bf1f07bac8 | |
| parent | 058a7810d171c17407d6f0e39d155ad9a115f9dd (diff) | |
msm: mdss: add retire fence support
Retire fence timeline should follow the actual panel vsync more closely.
For video mode the retire fence can be signaled along with the release
timeline with +1 offset, since this is signaled at vsync.
In case of command mode the panel vsync can be different from buffer
release timeline. To handle the command mode panel vsyncs better, create
a new timeline for the retire fence signaling.
Change-Id: If8a1eb717d733ca215275a8be4f0054091dbc147
Signed-off-by: Adrian Salido-Moreno <adrianm@codeaurora.org>
[cip@codeaurora.org: Update sw_sync.h include]
Signed-off-by: Clarence Ip <cip@codeaurora.org>
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_fb.c | 116 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_fb.h | 7 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp.h | 5 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_overlay.c | 108 | ||||
| -rw-r--r-- | include/uapi/linux/msm_mdp.h | 10 |
5 files changed, 207 insertions, 39 deletions
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index 37c918683c22..aefb80037488 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -445,17 +445,27 @@ static int mdss_fb_probe(struct platform_device *pdev) mfd->mdp_sync_pt_data.timeline = sw_sync_timeline_create(timeline_name); if (mfd->mdp_sync_pt_data.timeline == NULL) { - pr_err("%s: cannot create time line", __func__); + pr_err("cannot create release fence time line\n"); return -ENOMEM; } mfd->mdp_sync_pt_data.notifier.notifier_call = __mdss_fb_sync_buf_done_callback; } - if ((mfd->panel.type == WRITEBACK_PANEL) || - (mfd->panel.type == MIPI_CMD_PANEL)) + + switch (mfd->panel.type) { + case WRITEBACK_PANEL: mfd->mdp_sync_pt_data.threshold = 1; - else + mfd->mdp_sync_pt_data.retire_threshold = 0; + break; + case MIPI_CMD_PANEL: + mfd->mdp_sync_pt_data.threshold = 1; + mfd->mdp_sync_pt_data.retire_threshold = 1; + break; + default: mfd->mdp_sync_pt_data.threshold = 2; + mfd->mdp_sync_pt_data.retire_threshold = 0; + break; + } if (mfd->splash_logo_enabled) { mfd->splash_thread = kthread_run(mdss_fb_splash_thread, mfd, @@ -2042,42 +2052,37 @@ static int mdss_fb_set_lut(struct fb_info *info, void __user *p) } /** - * mdss_fb_sync_get_rel_fence() - get release fence from sync pt timeline - * @sync_pt_data: Sync pt structure holding timeline and fence info. + * mdss_fb_sync_get_fence() - get fence from timeline + * @timeline: Timeline to create the fence on + * @fence_name: Name of the fence that will be created for debugging + * @val: Timeline value at which the fence will be signaled * - * Function returns a release fence on the timeline associated with the - * sync pt struct given and it's associated information. The release fence - * created can be used to signal when buffers provided will be released. + * Function returns a fence on the timeline given with the name provided. + * The fence created will be signaled when the timeline is advanced. */ -static struct sync_fence *__mdss_fb_sync_get_rel_fence( - struct msm_sync_pt_data *sync_pt_data) +struct sync_fence *mdss_fb_sync_get_fence(struct sw_sync_timeline *timeline, + const char *fence_name, int val) { - struct sync_pt *rel_sync_pt; - struct sync_fence *rel_fence; - int val; + struct sync_pt *sync_pt; + struct sync_fence *fence; - val = sync_pt_data->timeline_value + sync_pt_data->threshold + - atomic_read(&sync_pt_data->commit_cnt); + pr_debug("%s: buf sync fence timeline=%d\n", fence_name, val); - pr_debug("%s: buf sync rel fence timeline=%d\n", - sync_pt_data->fence_name, val); - - rel_sync_pt = sw_sync_pt_create(sync_pt_data->timeline, val); - if (rel_sync_pt == NULL) { - pr_err("%s: cannot create sync point\n", - sync_pt_data->fence_name); + sync_pt = sw_sync_pt_create(timeline, val); + if (sync_pt == NULL) { + pr_err("%s: cannot create sync point\n", fence_name); return NULL; } /* create fence */ - rel_fence = sync_fence_create(sync_pt_data->fence_name, rel_sync_pt); - if (rel_fence == NULL) { - sync_pt_free(rel_sync_pt); - pr_err("%s: cannot create fence\n", sync_pt_data->fence_name); + fence = sync_fence_create(fence_name, sync_pt); + if (fence == NULL) { + sync_pt_free(sync_pt); + pr_err("%s: cannot create fence\n", fence_name); return NULL; } - return rel_fence; + return fence; } static int mdss_fb_handle_buf_sync_ioctl(struct msm_sync_pt_data *sync_pt_data, @@ -2085,8 +2090,10 @@ static int mdss_fb_handle_buf_sync_ioctl(struct msm_sync_pt_data *sync_pt_data, { int i, ret = 0; int acq_fen_fd[MDP_MAX_FENCE_FD]; - struct sync_fence *fence, *rel_fence; + struct sync_fence *fence, *rel_fence, *retire_fence; int rel_fen_fd; + int retire_fen_fd; + int val; if ((buf_sync->acq_fen_fd_cnt > MDP_MAX_FENCE_FD) || (sync_pt_data->timeline == NULL)) @@ -2121,7 +2128,12 @@ static int mdss_fb_handle_buf_sync_ioctl(struct msm_sync_pt_data *sync_pt_data, if (ret) goto buf_sync_err_1; - rel_fence = __mdss_fb_sync_get_rel_fence(sync_pt_data); + val = sync_pt_data->timeline_value + sync_pt_data->threshold + + atomic_read(&sync_pt_data->commit_cnt); + + /* Set release fence */ + rel_fence = mdss_fb_sync_get_fence(sync_pt_data->timeline, + sync_pt_data->fence_name, val); if (IS_ERR_OR_NULL(rel_fence)) { pr_err("%s: unable to retrieve release fence\n", sync_pt_data->fence_name); @@ -2145,6 +2157,50 @@ static int mdss_fb_handle_buf_sync_ioctl(struct msm_sync_pt_data *sync_pt_data, pr_err("%s: copy_to_user failed\n", sync_pt_data->fence_name); goto buf_sync_err_3; } + + if (!(buf_sync->flags & MDP_BUF_SYNC_FLAG_RETIRE_FENCE)) + goto skip_retire_fence; + + if (sync_pt_data->get_retire_fence) + retire_fence = sync_pt_data->get_retire_fence(sync_pt_data); + else + retire_fence = NULL; + + if (IS_ERR_OR_NULL(retire_fence)) { + val += sync_pt_data->retire_threshold; + retire_fence = mdss_fb_sync_get_fence( + sync_pt_data->timeline, "mdp-retire", val); + } + + if (IS_ERR_OR_NULL(retire_fence)) { + pr_err("%s: unable to retrieve retire fence\n", + sync_pt_data->fence_name); + ret = retire_fence ? PTR_ERR(rel_fence) : -ENOMEM; + goto buf_sync_err_3; + } + retire_fen_fd = get_unused_fd_flags(0); + + if (retire_fen_fd < 0) { + pr_err("%s: get_unused_fd_flags failed for retire fence\n", + sync_pt_data->fence_name); + ret = -EIO; + sync_fence_put(retire_fence); + goto buf_sync_err_3; + } + + sync_fence_install(retire_fence, retire_fen_fd); + + ret = copy_to_user(buf_sync->retire_fen_fd, &retire_fen_fd, + sizeof(int)); + if (ret) { + pr_err("%s: copy_to_user failed for retire fence\n", + sync_pt_data->fence_name); + put_unused_fd(retire_fen_fd); + sync_fence_put(retire_fence); + goto buf_sync_err_3; + } + +skip_retire_fence: mutex_unlock(&sync_pt_data->sync_mutex); if (buf_sync->flags & MDP_BUF_SYNC_FLAG_WAIT) diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h index 0be1f9cb067c..c443d965e9b4 100644 --- a/drivers/video/fbdev/msm/mdss_fb.h +++ b/drivers/video/fbdev/msm/mdss_fb.h @@ -98,13 +98,16 @@ struct msm_sync_pt_data { struct sw_sync_timeline *timeline; int timeline_value; u32 threshold; - + u32 retire_threshold; atomic_t commit_cnt; bool flushed; bool async_wait_fences; struct mutex sync_mutex; struct notifier_block notifier; + + struct sync_fence *(*get_retire_fence) + (struct msm_sync_pt_data *sync_pt_data); }; struct msm_fb_data_type; @@ -248,6 +251,8 @@ void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl); void mdss_fb_update_backlight(struct msm_fb_data_type *mfd); int mdss_fb_wait_for_fence(struct msm_sync_pt_data *sync_pt_data); void mdss_fb_signal_timeline(struct msm_sync_pt_data *sync_pt_data); +struct sync_fence *mdss_fb_sync_get_fence(struct sw_sync_timeline *timeline, + const char *fence_name, int val); int mdss_fb_register_mdp_instance(struct msm_mdp_interface *mdp); int mdss_fb_dcm(struct msm_fb_data_type *mfd, int req_state); #endif /* MDSS_FB_H */ diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index aff7ef49ca20..05695c278d59 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -432,6 +432,11 @@ struct mdss_overlay_private { u32 splash_mem_addr; u32 splash_mem_size; u32 sd_enabled; + + struct sw_sync_timeline *vsync_timeline; + struct mdss_mdp_vsync_handler vsync_retire_handler; + struct work_struct retire_work; + int retire_cnt; }; /** diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 162f6a36cfd3..ebe0b41eacad 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -23,6 +23,7 @@ #include <linux/delay.h> #include <linux/msm_mdp.h> #include <linux/memblock.h> +#include <sw_sync.h> #include <linux/msm_iommu_domains.h> #include <mach/event_timer.h> @@ -2775,6 +2776,107 @@ static int mdss_mdp_overlay_splash_image(struct msm_fb_data_type *mfd, return rc; } +static void __vsync_retire_handle_vsync(struct mdss_mdp_ctl *ctl, ktime_t t) +{ + struct msm_fb_data_type *mfd = ctl->mfd; + struct mdss_overlay_private *mdp5_data; + + if (!mfd || !mfd->mdp.private1) { + pr_warn("Invalid handle for vsync\n"); + return; + } + + mdp5_data = mfd_to_mdp5_data(mfd); + schedule_work(&mdp5_data->retire_work); +} + +static void __vsync_retire_work_handler(struct work_struct *work) +{ + struct mdss_overlay_private *mdp5_data = + container_of(work, typeof(*mdp5_data), retire_work); + struct msm_sync_pt_data *sync_pt_data; + + if (!mdp5_data->ctl || !mdp5_data->ctl->mfd) + return; + + if (!mdp5_data->ctl->remove_vsync_handler) + return; + + sync_pt_data = &mdp5_data->ctl->mfd->mdp_sync_pt_data; + mutex_lock(&sync_pt_data->sync_mutex); + if (mdp5_data->retire_cnt > 0) { + sw_sync_timeline_inc(mdp5_data->vsync_timeline, 1); + + mdp5_data->retire_cnt--; + if (mdp5_data->retire_cnt == 0) { + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + mdp5_data->ctl->remove_vsync_handler(mdp5_data->ctl, + &mdp5_data->vsync_retire_handler); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + } + } + mutex_unlock(&sync_pt_data->sync_mutex); +} + +static struct sync_fence * +__vsync_retire_get_fence(struct msm_sync_pt_data *sync_pt_data) +{ + struct msm_fb_data_type *mfd; + struct mdss_overlay_private *mdp5_data; + struct mdss_mdp_ctl *ctl; + int rc, value; + + mfd = container_of(sync_pt_data, typeof(*mfd), mdp_sync_pt_data); + mdp5_data = mfd_to_mdp5_data(mfd); + + if (!mdp5_data || !mdp5_data->ctl) + return ERR_PTR(-ENODEV); + + ctl = mdp5_data->ctl; + if (!ctl->add_vsync_handler) + return ERR_PTR(-EOPNOTSUPP); + + if (!ctl->power_on) { + pr_debug("fb%d vsync pending first update\n", mfd->index); + return ERR_PTR(-EPERM); + } + + if (mdp5_data->retire_cnt == 0) { + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + rc = ctl->add_vsync_handler(ctl, + &mdp5_data->vsync_retire_handler); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + if (IS_ERR_VALUE(rc)) + return ERR_PTR(rc); + } + value = mdp5_data->vsync_timeline->value + 1 + mdp5_data->retire_cnt; + mdp5_data->retire_cnt++; + + return mdss_fb_sync_get_fence(mdp5_data->vsync_timeline, + "mdp-retire", value); +} + +static int __vsync_retire_setup(struct msm_fb_data_type *mfd) +{ + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + char name[24]; + + snprintf(name, sizeof(name), "mdss_fb%d_retire", mfd->index); + mdp5_data->vsync_timeline = sw_sync_timeline_create(name); + if (mdp5_data->vsync_timeline == NULL) { + pr_err("cannot vsync create time line"); + return -ENOMEM; + } + mfd->mdp_sync_pt_data.get_retire_fence = __vsync_retire_get_fence; + + mdp5_data->vsync_retire_handler.vsync_handler = + __vsync_retire_handle_vsync; + mdp5_data->vsync_retire_handler.cmd_post_flush = false; + INIT_WORK(&mdp5_data->retire_work, __vsync_retire_work_handler); + + return 0; +} + int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) { struct device *dev = mfd->fbi->dev; @@ -2846,6 +2948,12 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) pr_err("Error dfps sysfs creation ret=%d\n", rc); goto init_fail; } + } else if (mfd->panel_info->type == MIPI_CMD_PANEL) { + rc = __vsync_retire_setup(mfd); + if (IS_ERR_VALUE(rc)) { + pr_err("unable to create vsync timeline\n"); + goto init_fail; + } } mfd->mdp_sync_pt_data.async_wait_fences = true; diff --git a/include/uapi/linux/msm_mdp.h b/include/uapi/linux/msm_mdp.h index dc062cdc5cf8..e1d647f91791 100644 --- a/include/uapi/linux/msm_mdp.h +++ b/include/uapi/linux/msm_mdp.h @@ -973,6 +973,7 @@ struct msmfb_metadata { #define MDP_MAX_FENCE_FD 10 #define MDP_BUF_SYNC_FLAG_WAIT 1 +#define MDP_BUF_SYNC_FLAG_RETIRE_FENCE 0x10 struct mdp_buf_sync { uint32_t flags; @@ -980,6 +981,7 @@ struct mdp_buf_sync { uint32_t session_id; int *acq_fen_fd; int *rel_fen_fd; + int *retire_fen_fd; }; struct mdp_async_blit_req_list { @@ -989,19 +991,11 @@ struct mdp_async_blit_req_list { }; #define MDP_DISPLAY_COMMIT_OVERLAY 1 -struct mdp_buf_fence { - uint32_t flags; - uint32_t acq_fen_fd_cnt; - int acq_fen_fd[MDP_MAX_FENCE_FD]; - int rel_fen_fd[MDP_MAX_FENCE_FD]; -}; - struct mdp_display_commit { uint32_t flags; uint32_t wait_for_finish; struct fb_var_screeninfo var; - struct mdp_buf_fence buf_fence; struct mdp_rect roi; }; |
