From 21ce99e154b976279c5e4e60241aed65a426aa17 Mon Sep 17 00:00:00 2001 From: Xiaoming Zhou Date: Thu, 6 Nov 2014 13:38:14 -0500 Subject: [PATCH] msm: mdss: refactor the rotation logic Creating a rotation session manager to manage the rotation sessions. At creation time, rotation object is not assigned to the underlying hardware resources (i.e. pipe, mixer, controller, and write-back). The hardware assignment happens only at the time of commit, and released after the commit is done. Change-Id: I33bf625a177362953c11eefc2a779a5af409964b Signed-off-by: Xiaoming Zhou --- drivers/video/fbdev/msm/mdss_mdp.c | 6 + drivers/video/fbdev/msm/mdss_mdp_rotator.c | 893 +++++++++++++-------- drivers/video/fbdev/msm/mdss_mdp_rotator.h | 6 +- 3 files changed, 568 insertions(+), 337 deletions(-) diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index b90ab3e1850d..47f45e6d7092 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -53,6 +53,7 @@ #include "mdss_panel.h" #include "mdss_debug.h" #include "mdss_mdp_debug.h" +#include "mdss_mdp_rotator.h" #define CREATE_TRACE_POINTS #include "mdss_mdp_trace.h" @@ -1546,6 +1547,11 @@ static int mdss_mdp_probe(struct platform_device *pdev) pr_err("unable to register bus scaling\n"); goto probe_done; } + rc = mdss_mdp_rot_mgr_init(); + if (rc) { + pr_err("unable to initialize rotation mgr\n"); + goto probe_done; + } rc = mdss_mdp_debug_init(mdata); if (rc) { diff --git a/drivers/video/fbdev/msm/mdss_mdp_rotator.c b/drivers/video/fbdev/msm/mdss_mdp_rotator.c index 33144c6ea3e6..8d72f873a9d3 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_rotator.c +++ b/drivers/video/fbdev/msm/mdss_mdp_rotator.c @@ -26,70 +26,411 @@ #include "mdss_fb.h" #include "mdss_debug.h" -#define MAX_ROTATOR_SESSIONS 8 +#define MAX_ROTATOR_PIPE_COUNT 2 +#define PIPE_ACQUIRE_TIMEOUT_IN_MS 400 -static DEFINE_MUTEX(rotator_lock); -static struct mdss_mdp_rotator_session rotator_session[MAX_ROTATOR_SESSIONS]; -static LIST_HEAD(rotator_queue); +#define MAX_ROTATOR_SESSION_ID 0xfffffff +struct mdss_mdp_rot_pipe { + struct mdss_mdp_pipe *pipe; + struct mdss_mdp_rotator_session *active_session; + struct mdss_mdp_rotator_session *previous_session; + struct completion comp; + bool context_switched; + u32 current_smp_size; + u32 wait_count; +}; + +struct mdss_mdp_rot_session_mgr { + struct list_head queue; + struct mutex session_lock; + int session_id; + int session_count; + + int pipe_count; + struct mutex pipe_lock; + struct mdss_mdp_rot_pipe rot_pipes[MAX_ROTATOR_PIPE_COUNT]; + + struct workqueue_struct *rot_work_queue; +}; + +static struct mdss_mdp_rot_session_mgr *rot_mgr; static int mdss_mdp_rotator_finish(struct mdss_mdp_rotator_session *rot); static void mdss_mdp_rotator_commit_wq_handler(struct work_struct *work); -static int mdss_mdp_rotator_busy_wait(struct mdss_mdp_rotator_session *rot); +static int mdss_mdp_rotator_busy_wait(struct mdss_mdp_rotator_session *rot, + struct mdss_mdp_pipe *pipe); static int mdss_mdp_rotator_queue_helper(struct mdss_mdp_rotator_session *rot); static struct msm_sync_pt_data *mdss_mdp_rotator_sync_pt_create( struct mdss_mdp_rotator_session *rot); -static struct mdss_mdp_rotator_session *mdss_mdp_rotator_session_alloc(void) +int mdss_mdp_rot_mgr_init(void) +{ + if (rot_mgr) { + pr_debug("rot manager initialized already\n"); + return 0; + } + + rot_mgr = kzalloc(sizeof(struct mdss_mdp_rot_session_mgr), GFP_KERNEL); + if (!rot_mgr) { + pr_err("fail to allocate rot manager\n"); + return -ENOMEM; + } + + mutex_init(&rot_mgr->session_lock); + mutex_init(&rot_mgr->pipe_lock); + INIT_LIST_HEAD(&rot_mgr->queue); + rot_mgr->rot_work_queue = create_workqueue("rot_commit_workq"); + if (!rot_mgr->rot_work_queue) { + pr_err("fail to create rot commit work queue\n"); + kfree(rot_mgr); + rot_mgr = NULL; + return -ENOMEM; + } + + return 0; +} + +static int mdss_mdp_rot_mgr_pipe_get_count(void) +{ + int count; + + if (!rot_mgr) { + pr_err("rot manager not initialized\n"); + return -EINVAL; + } + + mutex_lock(&rot_mgr->pipe_lock); + count = rot_mgr->pipe_count; + mutex_unlock(&rot_mgr->pipe_lock); + + return count; +} + +static int mdss_mdp_rot_mgr_add_pipe(struct mdss_mdp_pipe *pipe) { - struct mdss_mdp_rotator_session *rot; int i; - for (i = 0; i < MAX_ROTATOR_SESSIONS; i++) { - rot = &rotator_session[i]; - if (rot->ref_cnt == 0) { - rot->ref_cnt++; - rot->session_id = i | MDSS_MDP_ROT_SESSION_MASK; - mutex_init(&rot->lock); - INIT_LIST_HEAD(&rot->head); - INIT_LIST_HEAD(&rot->list); - break; - } + if (!rot_mgr) { + pr_err("rot manager not initialized\n"); + return -EINVAL; } - if (i == MAX_ROTATOR_SESSIONS) { - pr_err("max rotator sessions reached\n"); + + mutex_lock(&rot_mgr->pipe_lock); + for (i = 0; i < MAX_ROTATOR_PIPE_COUNT; i++) { + if (!rot_mgr->rot_pipes[i].pipe) + break; + } + if (i >= MAX_ROTATOR_PIPE_COUNT) { + pr_err("max rotation pipe allocated\n"); + mutex_unlock(&rot_mgr->pipe_lock); + return -EINVAL; + } + + rot_mgr->pipe_count++; + rot_mgr->rot_pipes[i].pipe = pipe; + rot_mgr->rot_pipes[i].active_session = NULL; + rot_mgr->rot_pipes[i].previous_session = NULL; + rot_mgr->rot_pipes[i].context_switched = true; + rot_mgr->rot_pipes[i].current_smp_size = 0; + rot_mgr->rot_pipes[i].wait_count = 0; + init_completion(&rot_mgr->rot_pipes[i].comp); + complete(&rot_mgr->rot_pipes[i].comp); + mutex_unlock(&rot_mgr->pipe_lock); + + return 0; +} + +/** + * try to acquire a pipe in the pool, following this strategy: + * first, prefer to look for a free pipe which was used in the previous + * instance, this is to avoid unnecessary pipe context switch. + * second, look for any free pipe available + * third, try to look for a pipe that is in use. To avoid the situation where + * multiple rotation sessions waiting for the same pipe, a wait-count is used + * to keep track of the number of sessions waiting on the pipe, so that we + * can do the load balancing. + */ +static struct mdss_mdp_rot_pipe *mdss_mdp_rot_mgr_acquire_pipe( + struct mdss_mdp_rotator_session *rot) +{ + struct mdss_mdp_rot_pipe *rot_pipe = NULL; + struct mdss_mdp_rot_pipe *free_rot_pipe = NULL; + struct mdss_mdp_rot_pipe *busy_rot_pipe = NULL; + int ret, i; + + if (!rot_mgr) { + pr_err("rot manager not initialized\n"); return NULL; } + + mutex_lock(&rot_mgr->pipe_lock); + for (i = 0; i < MAX_ROTATOR_PIPE_COUNT; i++) { + rot_pipe = &rot_mgr->rot_pipes[i]; + + if (!rot_pipe->pipe) + continue; + + if (rot_pipe->active_session) { + if (!busy_rot_pipe || + (busy_rot_pipe->wait_count > + rot_pipe->wait_count)) + busy_rot_pipe = rot_pipe; + + continue; + } + + free_rot_pipe = rot_pipe; + + if (rot_pipe->previous_session == rot) + break; + } + + if (free_rot_pipe) { + free_rot_pipe->active_session = rot; + free_rot_pipe->context_switched = + (free_rot_pipe->previous_session != rot); + + rot_pipe = free_rot_pipe; + pr_debug("find a free pipe %p\n", rot_pipe->pipe); + } else { + rot_pipe = busy_rot_pipe; + pr_debug("find a busy pipe %p\n", rot_pipe->pipe); + } + + if (rot_pipe) + rot_pipe->wait_count++; + + mutex_unlock(&rot_mgr->pipe_lock); + + if (rot_pipe) { + ret = wait_for_completion_timeout(&rot_pipe->comp, + msecs_to_jiffies(PIPE_ACQUIRE_TIMEOUT_IN_MS)); + if (ret <= 0) { + pr_err("wait for pipe error = %d\n", ret); + mutex_lock(&rot_mgr->pipe_lock); + rot_pipe->wait_count--; + mutex_unlock(&rot_mgr->pipe_lock); + rot_pipe = NULL; + } + } + + return rot_pipe; +} + +static void mdss_mdp_rot_mgr_release_pipe(struct mdss_mdp_rot_pipe *pipe) +{ + if (!rot_mgr) { + pr_err("rot manager not initialized\n"); + return; + } + + mutex_lock(&rot_mgr->pipe_lock); + pipe->previous_session = pipe->active_session; + pipe->active_session = NULL; + pipe->wait_count--; + complete(&pipe->comp); + mutex_unlock(&rot_mgr->pipe_lock); +} + +static int mdss_mdp_rot_mgr_remove_free_pipe(void) +{ + struct mdss_mdp_pipe *pipe; + struct mdss_mdp_mixer *mixer; + struct mdss_mdp_ctl *tmp; + int i; + + if (!rot_mgr) { + pr_err("rot manager not initialized\n"); + return -EINVAL; + } + + for (i = 0; i < MAX_ROTATOR_PIPE_COUNT; i++) { + if (!rot_mgr->rot_pipes[i].pipe) + continue; + + if (!rot_mgr->rot_pipes[i].active_session) + break; + } + + if (i >= MAX_ROTATOR_PIPE_COUNT) { + pr_debug("cannot free any unused rotation pipe\n"); + return 0; + } + + pipe = rot_mgr->rot_pipes[i].pipe; + mixer = pipe->mixer_left; + mdss_mdp_pipe_destroy(pipe); + tmp = mdss_mdp_ctl_mixer_switch(mixer->ctl, + MDSS_MDP_WB_CTL_TYPE_BLOCK); + if (tmp) { + mixer = tmp->mixer_left; + mdss_mdp_wb_mixer_destroy(mixer); + } + + rot_mgr->rot_pipes[i].pipe = NULL; + rot_mgr->rot_pipes[i].active_session = NULL; + rot_mgr->rot_pipes[i].previous_session = NULL; + rot_mgr->pipe_count--; + + return 0; +} + +static int mdss_mdp_rot_mgr_get_id(u32 *id) +{ + if (!rot_mgr) { + pr_err("rot manager not initialized\n"); + return -EINVAL; + } + + mutex_lock(&rot_mgr->session_lock); + *id = rot_mgr->session_id | MDSS_MDP_ROT_SESSION_MASK; + rot_mgr->session_id++; + if (rot_mgr->session_id > MAX_ROTATOR_SESSION_ID) { + pr_debug("session id wrap around\n"); + rot_mgr->session_id = 0; + } + mutex_unlock(&rot_mgr->session_lock); + + return 0; +} + +static int mdss_mdp_rot_mgr_add_session(struct mdss_mdp_rotator_session *rot) +{ + if (!rot_mgr) { + pr_err("rot manager not initialized\n"); + return -EINVAL; + } + + mutex_lock(&rot_mgr->session_lock); + list_add_tail(&rot->head, &rot_mgr->queue); + rot_mgr->session_count++; + mutex_unlock(&rot_mgr->session_lock); + + return 0; +} + +static struct mdss_mdp_rotator_session + *mdss_mdp_rot_mgr_remove_first(void) +{ + struct mdss_mdp_rotator_session *rot; + + if (!rot_mgr) { + pr_err("rot manager not initialized\n"); + return NULL; + } + + mutex_lock(&rot_mgr->session_lock); + + rot = list_first_entry_or_null(&rot_mgr->queue, + struct mdss_mdp_rotator_session, head); + + if (rot) { + list_del(&rot->head); + rot_mgr->session_count--; + } + mutex_unlock(&rot_mgr->session_lock); + return rot; } -static inline struct mdss_mdp_rotator_session -*mdss_mdp_rotator_session_get(u32 session_id) +static void mdss_mdp_rot_mgr_del_session(struct mdss_mdp_rotator_session *rot) { - struct mdss_mdp_rotator_session *rot; - u32 ndx; - - ndx = session_id & ~MDSS_MDP_ROT_SESSION_MASK; - if (ndx < MAX_ROTATOR_SESSIONS) { - rot = &rotator_session[ndx]; - if (rot->ref_cnt && rot->session_id == session_id) - return rot; + if (!rot_mgr) { + pr_err("rot manager not initialized\n"); + return; } + + mutex_lock(&rot_mgr->session_lock); + list_del(&rot->head); + rot_mgr->session_count--; + + mutex_lock(&rot_mgr->pipe_lock); + if (rot_mgr->session_count < rot_mgr->pipe_count) + mdss_mdp_rot_mgr_remove_free_pipe(); + mutex_unlock(&rot_mgr->pipe_lock); + + mutex_unlock(&rot_mgr->session_lock); +} + + +static struct mdss_mdp_rotator_session + *mdss_mdp_rot_mgr_get_session(u32 session_id) +{ + struct mdss_mdp_rotator_session *rot = NULL; + + if (!rot_mgr) { + pr_err("rot manager not initialized\n"); + return NULL; + } + + mutex_lock(&rot_mgr->session_lock); + + if (!rot_mgr->session_count) { + mutex_unlock(&rot_mgr->session_lock); + return NULL; + } + + list_for_each_entry(rot, &rot_mgr->queue, head) { + if (rot->session_id == session_id) { + mutex_unlock(&rot_mgr->session_lock); + return rot; + } + } + + mutex_unlock(&rot_mgr->session_lock); return NULL; } +static struct mdss_mdp_rotator_session *mdss_mdp_rotator_session_alloc(void) +{ + struct mdss_mdp_rotator_session *rot; + + rot = kzalloc(sizeof(struct mdss_mdp_rotator_session), GFP_KERNEL); + if (!rot) { + pr_err("error allocating rotator session\n"); + return NULL; + } + + mutex_init(&rot->lock); + INIT_LIST_HEAD(&rot->head); + INIT_LIST_HEAD(&rot->list); + mdss_mdp_rot_mgr_get_id(&rot->session_id); + + return rot; +} + +static void mdss_mdp_rotator_session_free( + struct mdss_mdp_rotator_session *rot) +{ + if (!list_empty(&rot->list)) + list_del(&rot->list); + + kfree(rot->rot_sync_pt_data); + kfree(rot); +} + + struct msm_sync_pt_data *mdss_mdp_rotator_sync_pt_get( struct msm_fb_data_type *mfd, const struct mdp_buf_sync *buf_sync) { struct mdss_mdp_rotator_session *rot; - rot = mdss_mdp_rotator_session_get(buf_sync->session_id); + rot = mdss_mdp_rot_mgr_get_session(buf_sync->session_id); if (!rot) return NULL; + + /** + * check if we can use a singleton sync pt, + * instead of creating one for each rotation. + */ + mutex_lock(&rot->lock); if (!rot->rot_sync_pt_data) rot->rot_sync_pt_data = mdss_mdp_rotator_sync_pt_create(rot); if (rot->rot_sync_pt_data) rot->use_sync_pt = true; + mutex_unlock(&rot->lock); + return rot->rot_sync_pt_data; } @@ -116,24 +457,16 @@ static struct mdss_mdp_pipe *mdss_mdp_rotator_pipe_alloc(void) return pipe; } -static int mdss_mdp_rotator_busy_wait(struct mdss_mdp_rotator_session *rot) +static int mdss_mdp_rotator_busy_wait(struct mdss_mdp_rotator_session *rot, + struct mdss_mdp_pipe *pipe) { - - mutex_lock(&rot->lock); - if (!rot->pipe || !rot->pipe->mixer_left || - !rot->pipe->mixer_left->ctl) { - mutex_unlock(&rot->lock); - return -ENODEV; - } - if (rot->busy) { - struct mdss_mdp_ctl *ctl = rot->pipe->mixer_left->ctl; + struct mdss_mdp_ctl *ctl = pipe->mixer_left->ctl; mdss_mdp_display_wait4comp(ctl); rot->busy = false; if (ctl->shared_lock) mutex_unlock(ctl->shared_lock); } - mutex_unlock(&rot->lock); return 0; } @@ -148,61 +481,16 @@ static int mdss_mdp_rotator_kickoff(struct mdss_mdp_ctl *ctl, .priv_data = rot, }; - mutex_lock(&rot->lock); rot->busy = true; ret = mdss_mdp_writeback_display_commit(ctl, &wb_args); if (ret) { rot->busy = false; - pr_err("problem with kickoff rot pipe=%d", rot->pipe->num); + pr_err("problem with kickoff err=%d\n", ret); } - mutex_unlock(&rot->lock); + return ret; } -static int mdss_mdp_rotator_pipe_dequeue(struct mdss_mdp_rotator_session *rot) -{ - int rc; - if (rot->pipe) { - pr_debug("reusing existing session=%d\n", rot->pipe->num); - mdss_mdp_rotator_busy_wait(rot); - list_move_tail(&rot->head, &rotator_queue); - } else { - struct mdss_mdp_rotator_session *tmp; - - rot->params_changed++; - rot->pipe = mdss_mdp_rotator_pipe_alloc(); - if (rot->pipe) { - pr_debug("use new rotator pipe=%d\n", rot->pipe->num); - - list_add_tail(&rot->head, &rotator_queue); - } else if (!list_empty(&rotator_queue)) { - tmp = list_first_entry(&rotator_queue, - struct mdss_mdp_rotator_session, - head); - - rc = mdss_mdp_rotator_busy_wait(tmp); - mdss_mdp_smp_release(tmp->pipe); - list_del(&tmp->head); - if (rc) { - pr_err("no pipe attached to session=%d\n", - tmp->session_id); - return rc; - } else { - pr_debug("waited for rotator pipe=%d\n", - tmp->pipe->num); - } - rot->pipe = tmp->pipe; - tmp->pipe = NULL; - - list_add_tail(&rot->head, &rotator_queue); - } else { - pr_err("no available rotator pipes\n"); - return -EBUSY; - } - } - - return 0; -} /** * __mdss_mdp_rotator_to_pipe() - setup pipe according to rotator session params @@ -213,9 +501,13 @@ static int mdss_mdp_rotator_pipe_dequeue(struct mdss_mdp_rotator_session *rot) * to use rotator pipe. Note that this function assumes rotator pipe is idle. */ static int __mdss_mdp_rotator_to_pipe(struct mdss_mdp_rotator_session *rot, - struct mdss_mdp_pipe *pipe) + struct mdss_mdp_rot_pipe *rot_pipe) { + struct mdss_mdp_pipe *pipe = NULL; int ret; + int smp_count; + + pipe = rot_pipe->pipe; pipe->flags = rot->flags; pipe->src_fmt = mdss_mdp_get_format_params(rot->format); @@ -228,59 +520,57 @@ static int __mdss_mdp_rotator_to_pipe(struct mdss_mdp_rotator_session *rot, pipe->params_changed++; rot->params_changed = 0; + smp_count = mdss_mdp_smp_calc_num_blocks(pipe); + if (smp_count > rot_pipe->current_smp_size) + mdss_mdp_smp_release(pipe); + ret = mdss_mdp_smp_reserve(pipe); if (ret) { pr_debug("unable to mdss_mdp_smp_reserve rot data\n"); return ret; } + rot_pipe->current_smp_size = smp_count; return 0; } static int mdss_mdp_rotator_queue_sub(struct mdss_mdp_rotator_session *rot, - struct mdss_mdp_data *src_data, - struct mdss_mdp_data *dst_data) + struct mdss_mdp_rot_pipe *rot_pipe) { - struct mdss_mdp_pipe *rot_pipe = NULL; + struct mdss_mdp_data *src_data; + struct mdss_mdp_data *dst_data; + struct mdss_mdp_pipe *pipe; struct mdss_mdp_ctl *orig_ctl, *rot_ctl; int ret; - if (!rot || !rot->ref_cnt) - return -ENOENT; + src_data = &rot->src_buf; + dst_data = &rot->dst_buf; - ret = mdss_mdp_rotator_pipe_dequeue(rot); - if (ret) { - pr_err("unable to acquire rotator\n"); - return ret; - } + pipe = rot_pipe->pipe; - rot_pipe = rot->pipe; - - pr_debug("queue rotator pnum=%d\n", rot_pipe->num); - - orig_ctl = rot_pipe->mixer_left->ctl; + orig_ctl = pipe->mixer_left->ctl; if (orig_ctl->shared_lock) mutex_lock(orig_ctl->shared_lock); rot_ctl = mdss_mdp_ctl_mixer_switch(orig_ctl, - MDSS_MDP_WB_CTL_TYPE_BLOCK); + MDSS_MDP_WB_CTL_TYPE_BLOCK); if (!rot_ctl) { ret = -EINVAL; goto error; } else { - rot->pipe->mixer_left = rot_ctl->mixer_left; + pipe->mixer_left = rot_ctl->mixer_left; } - - if (rot->params_changed || rot_ctl->mdata->mixer_switched) { + if (rot->params_changed || rot_ctl->mdata->mixer_switched || + rot_pipe->context_switched) { ret = __mdss_mdp_rotator_to_pipe(rot, rot_pipe); if (ret) { pr_err("rotator session=%x to pipe=%d failed %d\n", - rot->session_id, rot_pipe->num, ret); + rot->session_id, pipe->num, ret); goto error; } } - ret = mdss_mdp_pipe_queue_data(rot_pipe, src_data); + ret = mdss_mdp_pipe_queue_data(pipe, src_data); if (ret) { pr_err("unable to queue rot data\n"); goto error; @@ -303,7 +593,7 @@ static void mdss_mdp_rotator_commit_wq_handler(struct work_struct *work) rot = container_of(work, struct mdss_mdp_rotator_session, commit_work); - mutex_lock(&rotator_lock); + mutex_lock(&rot->lock); ret = mdss_mdp_rotator_queue_helper(rot); if (ret) @@ -316,7 +606,7 @@ static void mdss_mdp_rotator_commit_wq_handler(struct work_struct *work) pr_err("rot_sync_pt_data is NULL\n"); } - mutex_unlock(&rotator_lock); + mutex_unlock(&rot->lock); } static struct msm_sync_pt_data *mdss_mdp_rotator_sync_pt_create( @@ -348,38 +638,32 @@ static struct msm_sync_pt_data *mdss_mdp_rotator_sync_pt_create( return sync_pt_data; } -static int mdss_mdp_rotator_busy_wait_ex(struct mdss_mdp_rotator_session *rot) +static int mdss_mdp_rotator_queue_helper(struct mdss_mdp_rotator_session *rot) { + int ret; + struct mdss_mdp_rot_pipe *rot_pipe; - struct mdss_mdp_rotator_session *tmp; - - for (tmp = rot; tmp; tmp = tmp->next) - mdss_mdp_rotator_busy_wait(tmp); + pr_debug("rotator session=%x start\n", rot->session_id); if (rot->use_sync_pt) mdss_fb_wait_for_fence(rot->rot_sync_pt_data); - return 0; -} - -static int mdss_mdp_rotator_queue_helper(struct mdss_mdp_rotator_session *rot) -{ - int ret; - struct mdss_mdp_rotator_session *tmp; - - pr_debug("rotator session=%x start\n", rot->session_id); - - for (ret = 0, tmp = rot; ret == 0 && tmp; tmp = tmp->next) - ret = mdss_mdp_rotator_queue_sub(tmp, - &rot->src_buf, &rot->dst_buf); + rot_pipe = mdss_mdp_rot_mgr_acquire_pipe(rot); + if (!rot_pipe) { + pr_err("fail to get pipe for session = %d\n", rot->session_id); + return -EINVAL; + } + ret = mdss_mdp_rotator_queue_sub(rot, rot_pipe); if (ret) { pr_err("rotation failed %d for rot=%d\n", ret, rot->session_id); + mdss_mdp_rot_mgr_release_pipe(rot_pipe); return ret; } - for (tmp = rot; tmp; tmp = tmp->next) - mdss_mdp_rotator_busy_wait(tmp); + mdss_mdp_rotator_busy_wait(rot, rot_pipe->pipe); + + mdss_mdp_rot_mgr_release_pipe(rot_pipe); return ret; } @@ -389,7 +673,7 @@ static int mdss_mdp_rotator_queue(struct mdss_mdp_rotator_session *rot) int ret = 0; if (rot->use_sync_pt) - schedule_work(&rot->commit_work); + queue_work(rot_mgr->rot_work_queue, &rot->commit_work); else ret = mdss_mdp_rotator_queue_helper(rot); @@ -398,46 +682,7 @@ static int mdss_mdp_rotator_queue(struct mdss_mdp_rotator_session *rot) return ret; } -/* - * Try to reserve hardware resources for rotator session if possible, if this - * is not possible we may still have a chance to reuse existing pipes used by - * other sessions at a later point. - */ -static int __mdss_mdp_rotator_pipe_reserve(struct mdss_mdp_rotator_session *rot) -{ - int ret; - - if (!rot->pipe) { - rot->pipe = mdss_mdp_rotator_pipe_alloc(); - if (rot->pipe) { - pr_debug("reserved rotator pipe=%d\n", rot->pipe->num); - list_add_tail(&rot->head, &rotator_queue); - } else { - /* - * if rotator queue is not empty means that we'll be - * able to reuse existing rotator pipes for this rotator - * session, otherwise it means that there are no DMA - * pipes available so we should abort now - */ - if (list_empty(&rotator_queue)) { - pr_err("unable to reserve rot pipe\n"); - return -ENODEV; - } - - pr_debug("unable to get rot pipe but some in queue\n"); - return 0; - } - } - - ret = __mdss_mdp_rotator_to_pipe(rot, rot->pipe); - if (ret) - pr_err("rotator session=%x to pipe=%d failed %d\n", - rot->session_id, rot->pipe->num, ret); - - return ret; -} - -int mdss_mdp_calc_dnsc_factor(struct mdp_overlay *req, +static int mdss_mdp_calc_dnsc_factor(struct mdp_overlay *req, struct mdss_mdp_rotator_session *rot) { int ret = 0; @@ -491,61 +736,13 @@ dnsc_err: return ret; } -int mdss_mdp_rotator_setup(struct msm_fb_data_type *mfd, - struct mdp_overlay *req) +static int mdss_mdp_rotator_config(struct msm_fb_data_type *mfd, + struct mdss_mdp_rotator_session *rot, + struct mdp_overlay *req, + struct mdss_mdp_format_params *fmt) { struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); - struct mdss_mdp_rotator_session *rot = NULL; - struct mdss_mdp_format_params *fmt; u32 bwc_enabled; - bool format_changed = false; - int ret = 0; - - mutex_lock(&rotator_lock); - - fmt = mdss_mdp_get_format_params(req->src.format); - if (!fmt) { - pr_err("invalid rot format %d\n", req->src.format); - ret = -EINVAL; - goto rot_err; - } - - ret = mdss_mdp_overlay_req_check(mfd, req, fmt); - if (ret) - goto rot_err; - - if (req->id == MSMFB_NEW_REQUEST) { - rot = mdss_mdp_rotator_session_alloc(); - if (!rot) { - pr_err("unable to allocate rotator session\n"); - ret = -ENOMEM; - goto rot_err; - } - - rot->pid = current->tgid; - list_add(&rot->list, &mdp5_data->rot_proc_list); - } else if (req->id & MDSS_MDP_ROT_SESSION_MASK) { - rot = mdss_mdp_rotator_session_get(req->id); - if (!rot) { - pr_err("rotator session=%x not found\n", req->id); - ret = -ENODEV; - goto rot_err; - } - - if (work_pending(&rot->commit_work)) { - mutex_unlock(&rotator_lock); - flush_work(&rot->commit_work); - mutex_lock(&rotator_lock); - } - - if (rot->format != fmt->format) - format_changed = true; - - } else { - pr_err("invalid rotator session id=%x\n", req->id); - ret = -EINVAL; - goto rot_err; - } /* keep only flags of interest to rotator */ rot->flags = req->flags & (MDP_ROT_90 | MDP_FLIP_LR | MDP_FLIP_UD | @@ -570,15 +767,13 @@ int mdss_mdp_rotator_setup(struct msm_fb_data_type *mfd, if (mdp5_data->mdata->has_rot_dwnscale && mdss_mdp_calc_dnsc_factor(req, rot)) { pr_err("Error calculating dnsc_factor\n"); - ret = -EINVAL; - goto rot_err; + return -EINVAL; } if ((req->flags & MDP_DEINTERLACE) && (rot->dnsc_factor_w || rot->dnsc_factor_h)) { pr_err("Downscale not supported with interlaced content\n"); - ret = -EINVAL; - goto rot_err; + return -EINVAL; } if (req->flags & MDP_DEINTERLACE) { @@ -600,99 +795,141 @@ int mdss_mdp_rotator_setup(struct msm_fb_data_type *mfd, if (rot->flags & MDP_ROT_90) swap(rot->dst.w, rot->dst.h); - rot->params_changed++; - - /* If the format changed, release the smp alloc */ - if (format_changed && rot->pipe) { - mdss_mdp_rotator_busy_wait(rot); - mdss_mdp_smp_release(rot->pipe); - } - - ret = __mdss_mdp_rotator_pipe_reserve(rot); - if (!ret && rot->next) - ret = __mdss_mdp_rotator_pipe_reserve(rot->next); - - if (ret) - goto rot_err; - - req->id = rot->session_id; - - rot_err: - if (ret) { - pr_err("Unable to setup rotator session\n"); - if (rot && (req->id == MSMFB_NEW_REQUEST)) - mdss_mdp_rotator_finish(rot); - } - /* - * overwrite the src format for rotator to dst format - * for use by the user. On subsequent set calls, the - * user is expected to proivde the original src format - */ req->src.format = mdss_mdp_get_rotator_dst_format(req->src.format, req->flags & MDP_ROT_90, req->flags & MDP_BWC_EN); - mutex_unlock(&rotator_lock); + rot->params_changed++; + + return 0; +} + +static int mdss_mdp_rotator_create(struct msm_fb_data_type *mfd, + struct mdp_overlay *req, + struct mdss_mdp_format_params *fmt) +{ + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + struct mdss_mdp_rotator_session *rot; + struct mdss_mdp_pipe *pipe; + int pipe_count; + int ret; + + rot = mdss_mdp_rotator_session_alloc(); + if (!rot) { + pr_err("unable to allocate rotator session\n"); + return -ENOMEM; + } + + mutex_lock(&rot->lock); + + rot->pid = current->tgid; + list_add(&rot->list, &mdp5_data->rot_proc_list); + + req->id = rot->session_id; + + ret = mdss_mdp_rot_mgr_add_session(rot); + if (ret) { + pr_err("fail to add rotation to session mgr\n"); + goto rotator_new_setup_add_err; + } + + ret = mdss_mdp_rotator_config(mfd, rot, req, fmt); + if (ret) { + pr_err("fail to config the rotation object\n"); + goto rotator_new_setup_pipe_alloc_err; + } + + pipe_count = mdss_mdp_rot_mgr_pipe_get_count(); + + if (pipe_count < MAX_ROTATOR_PIPE_COUNT) { + pipe = mdss_mdp_rotator_pipe_alloc(); + if (pipe) { + mdss_mdp_rot_mgr_add_pipe(pipe); + pipe_count++; + } + } + + if (!pipe_count) { + pr_err("no pipe available for the rotation session\n"); + ret = -EBUSY; + goto rotator_new_setup_pipe_alloc_err; + } + + mutex_unlock(&rot->lock); + return 0; + +rotator_new_setup_pipe_alloc_err: + mdss_mdp_rot_mgr_del_session(rot); +rotator_new_setup_add_err: + mutex_unlock(&rot->lock); + mdss_mdp_rotator_session_free(rot); + return ret; +} + +static int mdss_mdp_rotator_config_ex(struct msm_fb_data_type *mfd, + struct mdp_overlay *req, + struct mdss_mdp_format_params *fmt) +{ + struct mdss_mdp_rotator_session *rot; + int ret; + + rot = mdss_mdp_rot_mgr_get_session(req->id); + if (!rot) { + pr_err("rotator session=%x not found\n", req->id); + return -ENODEV; + } + + mutex_lock(&rot->lock); + ret = mdss_mdp_rotator_config(mfd, rot, req, fmt); + mutex_unlock(&rot->lock); + return ret; +} + +int mdss_mdp_rotator_setup(struct msm_fb_data_type *mfd, + struct mdp_overlay *req) +{ + struct mdss_mdp_format_params *fmt; + int ret = 0; + + fmt = mdss_mdp_get_format_params(req->src.format); + if (!fmt) { + pr_err("invalid rot format %d\n", req->src.format); + return -EINVAL; + } + + ret = mdss_mdp_overlay_req_check(mfd, req, fmt); + if (ret) { + pr_err("rotator failed the overlay request check\n"); + return ret; + } + + if (req->id == MSMFB_NEW_REQUEST) { + ret = mdss_mdp_rotator_create(mfd, req, fmt); + } else if (req->id & MDSS_MDP_ROT_SESSION_MASK) { + ret = mdss_mdp_rotator_config_ex(mfd, req, fmt); + } else { + pr_err("invalid rotator session id=%x\n", req->id); + return -EINVAL; + } + return ret; } static int mdss_mdp_rotator_finish(struct mdss_mdp_rotator_session *rot) { - struct mdss_mdp_pipe *rot_pipe; - struct mdss_mdp_ctl *tmp; - int ret = 0; - struct msm_sync_pt_data *rot_sync_pt_data; - struct work_struct commit_work; - - if (!rot) - return -ENODEV; - pr_debug("finish rot id=%x\n", rot->session_id); - if (rot->next) - mdss_mdp_rotator_finish(rot->next); + cancel_work_sync(&rot->commit_work); + mdss_mdp_rot_mgr_del_session(rot); - rot_pipe = rot->pipe; - if (rot_pipe) { - if (work_pending(&rot->commit_work)) { - mutex_unlock(&rotator_lock); - cancel_work_sync(&rot->commit_work); - mutex_lock(&rotator_lock); - } - - mdss_mdp_rotator_busy_wait(rot); - list_del(&rot->head); - } - - if (!list_empty(&rot->list)) - list_del(&rot->list); - - rot_sync_pt_data = rot->rot_sync_pt_data; - commit_work = rot->commit_work; - memset(rot, 0, sizeof(*rot)); - rot->rot_sync_pt_data = rot_sync_pt_data; - rot->commit_work = commit_work; - - if (rot_pipe) { - struct mdss_mdp_mixer *mixer = rot_pipe->mixer_left; - mdss_mdp_pipe_destroy(rot_pipe); - tmp = mdss_mdp_ctl_mixer_switch(mixer->ctl, - MDSS_MDP_WB_CTL_TYPE_BLOCK); - if (!tmp) - return -EINVAL; - else - mixer = tmp->mixer_left; - mdss_mdp_wb_mixer_destroy(mixer); - } - return ret; + return 0; } int mdss_mdp_rotator_release(struct mdss_mdp_rotator_session *rot) { - int rc = 0; + int rc; - mutex_lock(&rotator_lock); rc = mdss_mdp_rotator_finish(rot); - mutex_unlock(&rotator_lock); + mdss_mdp_rotator_session_free(rot); return rc; } @@ -700,20 +937,14 @@ int mdss_mdp_rotator_release(struct mdss_mdp_rotator_session *rot) int mdss_mdp_rotator_release_all(void) { struct mdss_mdp_rotator_session *rot; - int i, cnt; - mutex_lock(&rotator_lock); - for (i = 0, cnt = 0; i < MAX_ROTATOR_SESSIONS; i++) { - rot = &rotator_session[i]; - if (rot->ref_cnt) { - mdss_mdp_rotator_finish(rot); - cnt++; - } + while (true) { + rot = mdss_mdp_rot_mgr_remove_first(); + if (!rot) + break; + + mdss_mdp_rotator_finish(rot); } - mutex_unlock(&rotator_lock); - - if (cnt) - pr_debug("cleaned up %d rotator sessions\n", cnt); return 0; } @@ -726,24 +957,20 @@ int mdss_mdp_rotator_play(struct msm_fb_data_type *mfd, u32 flgs; struct mdss_mdp_data src_buf; - mutex_lock(&rotator_lock); - rot = mdss_mdp_rotator_session_get(req->id); + rot = mdss_mdp_rot_mgr_get_session(req->id); if (!rot) { pr_err("invalid session id=%x\n", req->id); - ret = -ENOENT; - goto session_fail; + return -ENOENT; } memset(&src_buf, 0, sizeof(struct mdss_mdp_data)); flgs = rot->flags & MDP_SECURE_OVERLAY_SESSION; + flush_work(&rot->commit_work); + mdss_iommu_ctrl(1); - ret = mdss_mdp_rotator_busy_wait_ex(rot); - if (ret) { - pr_err("rotator busy wait error\n"); - goto dst_buf_fail; - } + mutex_lock(&rot->lock); ret = mdss_mdp_data_get(&src_buf, &req->data, 1, flgs, &mfd->pdev->dev); @@ -784,9 +1011,8 @@ int mdss_mdp_rotator_play(struct msm_fb_data_type *mfd, pr_err("rotator queue error session id=%x\n", req->id); dst_buf_fail: + mutex_unlock(&rot->lock); mdss_iommu_ctrl(0); -session_fail: - mutex_unlock(&rotator_lock); return ret; } @@ -794,15 +1020,14 @@ int mdss_mdp_rotator_unset(int ndx) { struct mdss_mdp_rotator_session *rot; int ret = 0; - mutex_lock(&rotator_lock); - rot = mdss_mdp_rotator_session_get(ndx); + + rot = mdss_mdp_rot_mgr_get_session(ndx); if (rot) { + ret = mdss_mdp_rotator_finish(rot); mdss_mdp_data_free(&rot->src_buf); mdss_mdp_data_free(&rot->dst_buf); - - rot->pid = 0; - ret = mdss_mdp_rotator_finish(rot); + mdss_mdp_rotator_session_free(rot); } - mutex_unlock(&rotator_lock); + return ret; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_rotator.h b/drivers/video/fbdev/msm/mdss_mdp_rotator.h index b3360c0e2fc2..9d84a3d32613 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_rotator.h +++ b/drivers/video/fbdev/msm/mdss_mdp_rotator.h @@ -21,7 +21,6 @@ struct mdss_mdp_rotator_session { u32 session_id; - u32 ref_cnt; u32 params_changed; int pid; @@ -36,7 +35,6 @@ struct mdss_mdp_rotator_session { u32 dnsc_factor_h; u32 bwc_mode; - struct mdss_mdp_pipe *pipe; struct mutex lock; u8 busy; @@ -48,7 +46,6 @@ struct mdss_mdp_rotator_session { bool use_sync_pt; struct list_head head; struct list_head list; - struct mdss_mdp_rotator_session *next; struct msm_sync_pt_data *rot_sync_pt_data; struct work_struct commit_work; }; @@ -92,4 +89,7 @@ struct msm_sync_pt_data *mdss_mdp_rotator_sync_pt_get( int mdss_mdp_rotator_play(struct msm_fb_data_type *mfd, struct msmfb_overlay_data *req); int mdss_mdp_rotator_unset(int ndx); + +int mdss_mdp_rot_mgr_init(void); + #endif /* MDSS_MDP_ROTATOR_H */