diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c index ff3fe859a798..2101d4f67659 100644 --- a/drivers/video/msm/msm_fb.c +++ b/drivers/video/msm/msm_fb.c @@ -113,6 +113,8 @@ static int msm_fb_mmap(struct fb_info *info, struct vm_area_struct * vma); static int mdp_bl_scale_config(struct msm_fb_data_type *mfd, struct mdp_bl_scale_data *data); static void msm_fb_scale_bl(__u32 *bl_lvl); +static void msm_fb_commit_wq_handler(struct work_struct *work); +static int msm_fb_pan_idle(struct msm_fb_data_type *mfd); #ifdef CONFIG_LGE_DISP_FBREAD static ssize_t msm_fb_read(struct fb_info *info, char __user *buf, @@ -990,7 +992,7 @@ static void msm_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; - + msm_fb_pan_idle(mfd); cfb_fillrect(info, rect); if (!mfd->hw_refresh && (info->var.yoffset == 0) && !mfd->sw_currently_refreshing) { @@ -1011,6 +1013,7 @@ static void msm_fb_copyarea(struct fb_info *info, { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + msm_fb_pan_idle(mfd); cfb_copyarea(info, area); if (!mfd->hw_refresh && (info->var.yoffset == 0) && !mfd->sw_currently_refreshing) { @@ -1030,6 +1033,7 @@ static void msm_fb_imageblit(struct fb_info *info, const struct fb_image *image) { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + msm_fb_pan_idle(mfd); cfb_imageblit(info, image); if (!mfd->hw_refresh && (info->var.yoffset == 0) && !mfd->sw_currently_refreshing) { @@ -1055,6 +1059,7 @@ static int msm_fb_blank(int blank_mode, struct fb_info *info) event.data = &blank_mode; fb_notifier_call_chain(FB_EVENT_BLANK, &event); } + msm_fb_pan_idle(mfd); return msm_fb_blank_sub(blank_mode, info, mfd->op_enable); } @@ -1081,6 +1086,7 @@ static int msm_fb_mmap(struct fb_info *info, struct vm_area_struct * vma) u32 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); unsigned long off = vma->vm_pgoff << PAGE_SHIFT; struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + msm_fb_pan_idle(mfd); if (off >= len) { /* memory mapped io */ off -= len; @@ -1445,7 +1451,15 @@ static int msm_fb_register(struct msm_fb_data_type *mfd) mfd->msmfb_no_update_notify_timer.data = (unsigned long)mfd; init_completion(&mfd->msmfb_update_notify); init_completion(&mfd->msmfb_no_update_notify); - + init_completion(&mfd->commit_comp); + mutex_init(&mfd->sync_mutex); + INIT_WORK(&mfd->commit_work, msm_fb_commit_wq_handler); + mfd->msm_fb_backup = kzalloc(sizeof(struct msm_fb_backup_type), + GFP_KERNEL); + if (mfd->msm_fb_backup == 0) { + pr_err("error: not enough memory!\n"); + return -ENOMEM; + } fbram_offset = PAGE_ALIGN((int)fbram)-(int)fbram; fbram += fbram_offset; fbram_phys += fbram_offset; @@ -1772,18 +1786,83 @@ int msm_fb_wait_for_fence(struct msm_fb_data_type *mfd) } int msm_fb_signal_timeline(struct msm_fb_data_type *mfd) { + mutex_lock(&mfd->sync_mutex); if (mfd->timeline) { sw_sync_timeline_inc(mfd->timeline, 1); mfd->timeline_value++; } mfd->last_rel_fence = mfd->cur_rel_fence; mfd->cur_rel_fence = 0; + mutex_unlock(&mfd->sync_mutex); return 0; } DEFINE_SEMAPHORE(msm_fb_pan_sem); +static int msm_fb_pan_idle(struct msm_fb_data_type *mfd) +{ + int ret = 0; + + mutex_lock(&mfd->sync_mutex); + if (mfd->is_committing) { + mutex_unlock(&mfd->sync_mutex); + ret = wait_for_completion_timeout(&mfd->commit_comp, + msecs_to_jiffies(WAIT_FENCE_TIMEOUT)); + if (ret <= 0) + pr_err("%s wait for commit_comp timeout %d %d", + __func__, ret, mfd->is_committing); + } else { + mutex_unlock(&mfd->sync_mutex); + } + return ret; +} static int msm_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct msm_fb_backup_type *fb_backup; + int ret = 0; + /* + * If framebuffer is 2, io pen display is not allowed. + */ + if (bf_supported && info->node == 2) { + pr_err("%s: no pan display for fb%d!", + __func__, info->node); + return -EPERM; + } + + if (info->node != 0 || mfd->cont_splash_done) /* primary */ + if ((!mfd->op_enable) || (!mfd->panel_power_on)) + return -EPERM; + + if (var->xoffset > (info->var.xres_virtual - info->var.xres)) + return -EINVAL; + + if (var->yoffset > (info->var.yres_virtual - info->var.yres)) + return -EINVAL; + msm_fb_pan_idle(mfd); + + mutex_lock(&mfd->sync_mutex); + + if (info->fix.xpanstep) + info->var.xoffset = + (var->xoffset / info->fix.xpanstep) * info->fix.xpanstep; + + if (info->fix.ypanstep) + info->var.yoffset = + (var->yoffset / info->fix.ypanstep) * info->fix.ypanstep; + + fb_backup = (struct msm_fb_backup_type *)mfd->msm_fb_backup; + memcpy(&fb_backup->info, info, sizeof(struct fb_info)); + memcpy(&fb_backup->var, var, sizeof(struct fb_var_screeninfo)); + mfd->is_committing = 1; + INIT_COMPLETION(mfd->commit_comp); + schedule_work(&mfd->commit_work); + mutex_unlock(&mfd->sync_mutex); + return ret; +} + +static int msm_fb_pan_display_sub(struct fb_var_screeninfo *var, + struct fb_info *info) { struct mdp_dirty_region dirty; struct mdp_dirty_region *dirtyPtr = NULL; @@ -1876,6 +1955,8 @@ static int msm_fb_pan_display(struct fb_var_screeninfo *var, mdp_set_dma_pan_info(info, dirtyPtr, (var->activate == FB_ACTIVATE_VBL)); + /* async call */ + mdp_dma_pan_update(info); msm_fb_signal_timeline(mfd); up(&msm_fb_pan_sem); @@ -1897,10 +1978,30 @@ static int msm_fb_pan_display(struct fb_var_screeninfo *var, return 0; } +static void msm_fb_commit_wq_handler(struct work_struct *work) +{ + struct msm_fb_data_type *mfd; + struct fb_var_screeninfo *var; + struct fb_info *info; + struct msm_fb_backup_type *fb_backup; + + mfd = container_of(work, struct msm_fb_data_type, commit_work); + fb_backup = (struct msm_fb_backup_type *)mfd->msm_fb_backup; + var = &fb_backup->var; + info = &fb_backup->info; + msm_fb_pan_display_sub(var, info); + mutex_lock(&mfd->sync_mutex); + mfd->is_committing = 0; + complete_all(&mfd->commit_comp); + mutex_unlock(&mfd->sync_mutex); + +} + static int msm_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + msm_fb_pan_idle(mfd); if (var->rotate != FB_ROTATE_UR) return -EINVAL; if (var->grayscale != info->var.grayscale) @@ -2031,7 +2132,7 @@ static int msm_fb_set_par(struct fb_info *info) struct fb_var_screeninfo *var = &info->var; int old_imgType; int blank = 0; - + msm_fb_pan_idle(mfd); old_imgType = mfd->fb_imgType; switch (var->bits_per_pixel) { case 16: @@ -3431,6 +3532,7 @@ static int msmfb_handle_buf_sync_ioctl(struct msm_fb_data_type *mfd, pr_err("%s:copy_from_user failed", __func__); return ret; } + mutex_lock(&mfd->sync_mutex); for (i = 0; i < buf_sync->acq_fen_fd_cnt; i++) { fence = sync_fence_fdget(acq_fen_fd[i]); if (fence == NULL) { @@ -3471,6 +3573,7 @@ static int msmfb_handle_buf_sync_ioctl(struct msm_fb_data_type *mfd, goto buf_sync_err_2; } mfd->acq_fen_cnt = buf_sync->acq_fen_fd_cnt; + mutex_unlock(&mfd->sync_mutex); return ret; buf_sync_err_2: sync_fence_put(mfd->cur_rel_fence); @@ -3481,6 +3584,7 @@ buf_sync_err_1: for (i = 0; i < fence_cnt; i++) sync_fence_put(mfd->acq_fen[i]); mfd->acq_fen_cnt = 0; + mutex_unlock(&mfd->sync_mutex); return ret; } static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd, @@ -3502,6 +3606,7 @@ static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd, struct msmfb_mdp_pp mdp_pp; struct mdp_buf_sync buf_sync; int ret = 0; + msm_fb_pan_idle(mfd); switch (cmd) { #ifdef CONFIG_FB_MSM_OVERLAY diff --git a/drivers/video/msm/msm_fb.h b/drivers/video/msm/msm_fb.h index da7213207722..777430d2b02d 100644 --- a/drivers/video/msm/msm_fb.h +++ b/drivers/video/msm/msm_fb.h @@ -195,6 +195,18 @@ struct msm_fb_data_type { struct sync_fence *last_rel_fence; struct sw_sync_timeline *timeline; int timeline_value; + u32 last_acq_fen_cnt; + struct sync_fence *last_acq_fen[MDP_MAX_FENCE_FD]; + struct mutex sync_mutex; + struct completion commit_comp; + u32 is_committing; + struct work_struct commit_work; + void *msm_fb_backup; +}; +struct msm_fb_backup_type { + struct fb_info info; + struct fb_var_screeninfo var; + struct msm_fb_data_type mfd; }; struct dentry *msm_fb_get_debugfs_root(void);