Loading drivers/video/msm/mdss/mdss_mdp_layer.c +69 −38 Original line number Diff line number Diff line Loading @@ -49,6 +49,12 @@ enum { MDSS_MDP_RETIRE_FENCE, }; enum layer_pipe_q { LAYER_USES_NEW_PIPE_Q = 0, LAYER_USES_USED_PIPE_Q, LAYER_USES_DESTROY_PIPE_Q, }; static inline bool is_layer_right_blend(struct mdp_rect *left_blend, struct mdp_rect *right_blend, u32 left_lm_w) { Loading Loading @@ -950,50 +956,65 @@ static bool __find_pipe_in_list(struct list_head *head, return false; } static struct mdss_mdp_pipe *__find_used_pipe(struct msm_fb_data_type *mfd, u32 pipe_ndx) /* * Search pipe from destroy and cleanup list to avoid validation failure. * It is caller responsibility to hold the list lock before calling this API. */ static struct mdss_mdp_pipe *__find_and_move_cleanup_pipe( struct mdss_overlay_private *mdp5_data, u32 pipe_ndx) { struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); struct mdss_mdp_pipe *pipe = NULL; bool found; mutex_lock(&mdp5_data->list_lock); found = __find_pipe_in_list(&mdp5_data->pipes_used, pipe_ndx, &pipe); /* check if the pipe is in the cleanup or destroy list */ if (!found && (__find_pipe_in_list(&mdp5_data->pipes_destroy, pipe_ndx, &pipe) || __find_pipe_in_list(&mdp5_data->pipes_cleanup, pipe_ndx, &pipe))) { pr_debug("reuse pipe%d ndx:%d\n", pipe->num, pipe->ndx); if (__find_pipe_in_list(&mdp5_data->pipes_destroy, pipe_ndx, &pipe)) { pr_debug("reuse destroy pipe id:%d ndx:%d\n", pipe->num, pipe_ndx); list_move(&pipe->list, &mdp5_data->pipes_used); } else if (__find_pipe_in_list(&mdp5_data->pipes_cleanup, pipe_ndx, &pipe)) { pr_debug("reuse cleanup pipe id:%d ndx:%d\n", pipe->num, pipe_ndx); mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_left); mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_right); pipe->mixer_stage = MDSS_MDP_STAGE_UNUSED; list_move(&pipe->list, &mdp5_data->pipes_used); } mutex_unlock(&mdp5_data->list_lock); return pipe; } /* * __assign_pipe_for_layer() - get a pipe for layer * * This function first searches the pipe from used list. On successful search, * it returns the same pipe for current layer. If pipe is switching mixer then * it will unstage it from current mixer. On failure search, it increments the * pipe refcount to allocate it for current cycle. * This function first searches the pipe from used list, cleanup list and * destroy list. On successful search, it returns the same pipe for current * layer. It also un-stage the pipe from current mixer for used, cleanup, * destroy pipes if they switches the mixer. On failure search, it returns * the null pipe. */ static struct mdss_mdp_pipe *__assign_pipe_for_layer( struct msm_fb_data_type *mfd, struct mdss_mdp_mixer *mixer, u32 pipe_ndx, bool *new_pipe) enum layer_pipe_q *pipe_q_type) { struct mdss_mdp_pipe *pipe; struct mdss_mdp_pipe *pipe = NULL; struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); struct mdss_data_type *mdata = mfd_to_mdata(mfd); pipe = __find_used_pipe(mfd, pipe_ndx); *new_pipe = pipe ? false : true; mutex_lock(&mdp5_data->list_lock); __find_pipe_in_list(&mdp5_data->pipes_used, pipe_ndx, &pipe); if (IS_ERR_OR_NULL(pipe)) { pipe = __find_and_move_cleanup_pipe(mdp5_data, pipe_ndx); if (IS_ERR_OR_NULL(pipe)) *pipe_q_type = LAYER_USES_NEW_PIPE_Q; else *pipe_q_type = LAYER_USES_DESTROY_PIPE_Q; } else { *pipe_q_type = LAYER_USES_USED_PIPE_Q; } mutex_unlock(&mdp5_data->list_lock); if (!*new_pipe) { /* found the pipe from used, destroy or cleanup list */ if (!IS_ERR_OR_NULL(pipe)) { if (pipe->mixer_left != mixer) { if (!mixer->ctl || (mixer->ctl->mfd != mfd)) { pr_err("Can't switch mixer %d->%d pnum %d!\n", Loading Loading @@ -1115,7 +1136,7 @@ static void __handle_free_list(struct mdss_overlay_private *mdp5_data, static int __validate_layers(struct msm_fb_data_type *mfd, struct file *file, struct mdp_layer_commit_v1 *commit) { int ret, i, release_ndx = 0, inputndx = 0; int ret, i, release_ndx = 0, inputndx = 0, destroy_ndx = 0; u32 left_lm_layers = 0, right_lm_layers = 0; u32 left_cnt = 0, right_cnt = 0; u32 left_lm_w = left_lm_w_from_mfd(mfd); Loading @@ -1130,7 +1151,7 @@ static int __validate_layers(struct msm_fb_data_type *mfd, struct mdss_mdp_mixer *mixer = NULL; struct mdp_input_layer *layer, *prev_layer, *layer_list; bool is_single_layer = false; bool new_pipe = false; enum layer_pipe_q pipe_q_type; ret = mutex_lock_interruptible(&mdp5_data->ov_lock); if (ret) Loading Loading @@ -1240,7 +1261,7 @@ static int __validate_layers(struct msm_fb_data_type *mfd, } pipe = __assign_pipe_for_layer(mfd, mixer, layer->pipe_ndx, &new_pipe); &pipe_q_type); if (IS_ERR_OR_NULL(pipe)) { pr_err("error assigning pipe id=0x%x rc:%ld\n", layer->pipe_ndx, PTR_ERR(pipe)); Loading @@ -1249,22 +1270,19 @@ static int __validate_layers(struct msm_fb_data_type *mfd, goto validate_exit; } if (pipe_q_type == LAYER_USES_NEW_PIPE_Q) release_ndx |= pipe->ndx; if (pipe_q_type == LAYER_USES_DESTROY_PIPE_Q) destroy_ndx |= pipe->ndx; ret = mdss_mdp_pipe_map(pipe); if (IS_ERR_VALUE(ret)) { pr_err("Unable to map used pipe%d ndx=%x\n", pipe->num, pipe->ndx); if (new_pipe) { if (!list_empty(&pipe->list)) list_del_init(&pipe->list); mdss_mdp_pipe_destroy(pipe); } layer->error_code = ret; goto validate_exit; } if (new_pipe) release_ndx |= pipe->ndx; ret = __configure_pipe_params(mfd, layer, pipe, left_blend_pipe, is_single_layer, mixer_mux); if (ret) { Loading Loading @@ -1302,11 +1320,11 @@ static int __validate_layers(struct msm_fb_data_type *mfd, ret = __validate_secure_display(mdp5_data); validate_exit: pr_debug("err=%d total_layer:%d left:%d right:%d release_ndx=0x%x processed=%d\n", pr_debug("err=%d total_layer:%d left:%d right:%d release_ndx=0x%x destroy_ndx=0x%x processed=%d\n", ret, layer_count, left_lm_layers, right_lm_layers, release_ndx, i); release_ndx, destroy_ndx, i); MDSS_XLOG(inputndx, layer_count, left_lm_layers, right_lm_layers, release_ndx, ret); release_ndx, destroy_ndx, ret); mutex_lock(&mdp5_data->list_lock); list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_used, list) { if (IS_ERR_VALUE(ret)) { Loading @@ -1317,6 +1335,15 @@ validate_exit: if (!list_empty(&pipe->list)) list_del_init(&pipe->list); mdss_mdp_pipe_destroy(pipe); } else if (pipe->ndx & destroy_ndx) { /* * cleanup/destroy list pipes should move back * to destroy list. Next/current kickoff cycle * will release the pipe because validate also * acquires ov_lock. */ list_move(&pipe->list, &mdp5_data->pipes_destroy); } } else { pipe->file = file; Loading Loading @@ -1591,13 +1618,17 @@ int mdss_mdp_async_position_update(struct msm_fb_data_type *mfd, struct mdss_mdp_pipe *pipe = NULL; struct mdp_async_layer *layer; struct mdss_rect dst, src; struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); u32 flush_bits = 0, inputndx = 0; mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); for (i = 0; i < update_pos->input_layer_cnt; i++) { layer = &update_pos->input_layers[i]; pipe = __find_used_pipe(mfd, layer->pipe_ndx); mutex_lock(&mdp5_data->list_lock); __find_pipe_in_list(&mdp5_data->pipes_used, layer->pipe_ndx, &pipe); mutex_unlock(&mdp5_data->list_lock); if (!pipe) { pr_err("invalid pipe ndx=0x%x for async update\n", layer->pipe_ndx); Loading Loading
drivers/video/msm/mdss/mdss_mdp_layer.c +69 −38 Original line number Diff line number Diff line Loading @@ -49,6 +49,12 @@ enum { MDSS_MDP_RETIRE_FENCE, }; enum layer_pipe_q { LAYER_USES_NEW_PIPE_Q = 0, LAYER_USES_USED_PIPE_Q, LAYER_USES_DESTROY_PIPE_Q, }; static inline bool is_layer_right_blend(struct mdp_rect *left_blend, struct mdp_rect *right_blend, u32 left_lm_w) { Loading Loading @@ -950,50 +956,65 @@ static bool __find_pipe_in_list(struct list_head *head, return false; } static struct mdss_mdp_pipe *__find_used_pipe(struct msm_fb_data_type *mfd, u32 pipe_ndx) /* * Search pipe from destroy and cleanup list to avoid validation failure. * It is caller responsibility to hold the list lock before calling this API. */ static struct mdss_mdp_pipe *__find_and_move_cleanup_pipe( struct mdss_overlay_private *mdp5_data, u32 pipe_ndx) { struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); struct mdss_mdp_pipe *pipe = NULL; bool found; mutex_lock(&mdp5_data->list_lock); found = __find_pipe_in_list(&mdp5_data->pipes_used, pipe_ndx, &pipe); /* check if the pipe is in the cleanup or destroy list */ if (!found && (__find_pipe_in_list(&mdp5_data->pipes_destroy, pipe_ndx, &pipe) || __find_pipe_in_list(&mdp5_data->pipes_cleanup, pipe_ndx, &pipe))) { pr_debug("reuse pipe%d ndx:%d\n", pipe->num, pipe->ndx); if (__find_pipe_in_list(&mdp5_data->pipes_destroy, pipe_ndx, &pipe)) { pr_debug("reuse destroy pipe id:%d ndx:%d\n", pipe->num, pipe_ndx); list_move(&pipe->list, &mdp5_data->pipes_used); } else if (__find_pipe_in_list(&mdp5_data->pipes_cleanup, pipe_ndx, &pipe)) { pr_debug("reuse cleanup pipe id:%d ndx:%d\n", pipe->num, pipe_ndx); mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_left); mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_right); pipe->mixer_stage = MDSS_MDP_STAGE_UNUSED; list_move(&pipe->list, &mdp5_data->pipes_used); } mutex_unlock(&mdp5_data->list_lock); return pipe; } /* * __assign_pipe_for_layer() - get a pipe for layer * * This function first searches the pipe from used list. On successful search, * it returns the same pipe for current layer. If pipe is switching mixer then * it will unstage it from current mixer. On failure search, it increments the * pipe refcount to allocate it for current cycle. * This function first searches the pipe from used list, cleanup list and * destroy list. On successful search, it returns the same pipe for current * layer. It also un-stage the pipe from current mixer for used, cleanup, * destroy pipes if they switches the mixer. On failure search, it returns * the null pipe. */ static struct mdss_mdp_pipe *__assign_pipe_for_layer( struct msm_fb_data_type *mfd, struct mdss_mdp_mixer *mixer, u32 pipe_ndx, bool *new_pipe) enum layer_pipe_q *pipe_q_type) { struct mdss_mdp_pipe *pipe; struct mdss_mdp_pipe *pipe = NULL; struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); struct mdss_data_type *mdata = mfd_to_mdata(mfd); pipe = __find_used_pipe(mfd, pipe_ndx); *new_pipe = pipe ? false : true; mutex_lock(&mdp5_data->list_lock); __find_pipe_in_list(&mdp5_data->pipes_used, pipe_ndx, &pipe); if (IS_ERR_OR_NULL(pipe)) { pipe = __find_and_move_cleanup_pipe(mdp5_data, pipe_ndx); if (IS_ERR_OR_NULL(pipe)) *pipe_q_type = LAYER_USES_NEW_PIPE_Q; else *pipe_q_type = LAYER_USES_DESTROY_PIPE_Q; } else { *pipe_q_type = LAYER_USES_USED_PIPE_Q; } mutex_unlock(&mdp5_data->list_lock); if (!*new_pipe) { /* found the pipe from used, destroy or cleanup list */ if (!IS_ERR_OR_NULL(pipe)) { if (pipe->mixer_left != mixer) { if (!mixer->ctl || (mixer->ctl->mfd != mfd)) { pr_err("Can't switch mixer %d->%d pnum %d!\n", Loading Loading @@ -1115,7 +1136,7 @@ static void __handle_free_list(struct mdss_overlay_private *mdp5_data, static int __validate_layers(struct msm_fb_data_type *mfd, struct file *file, struct mdp_layer_commit_v1 *commit) { int ret, i, release_ndx = 0, inputndx = 0; int ret, i, release_ndx = 0, inputndx = 0, destroy_ndx = 0; u32 left_lm_layers = 0, right_lm_layers = 0; u32 left_cnt = 0, right_cnt = 0; u32 left_lm_w = left_lm_w_from_mfd(mfd); Loading @@ -1130,7 +1151,7 @@ static int __validate_layers(struct msm_fb_data_type *mfd, struct mdss_mdp_mixer *mixer = NULL; struct mdp_input_layer *layer, *prev_layer, *layer_list; bool is_single_layer = false; bool new_pipe = false; enum layer_pipe_q pipe_q_type; ret = mutex_lock_interruptible(&mdp5_data->ov_lock); if (ret) Loading Loading @@ -1240,7 +1261,7 @@ static int __validate_layers(struct msm_fb_data_type *mfd, } pipe = __assign_pipe_for_layer(mfd, mixer, layer->pipe_ndx, &new_pipe); &pipe_q_type); if (IS_ERR_OR_NULL(pipe)) { pr_err("error assigning pipe id=0x%x rc:%ld\n", layer->pipe_ndx, PTR_ERR(pipe)); Loading @@ -1249,22 +1270,19 @@ static int __validate_layers(struct msm_fb_data_type *mfd, goto validate_exit; } if (pipe_q_type == LAYER_USES_NEW_PIPE_Q) release_ndx |= pipe->ndx; if (pipe_q_type == LAYER_USES_DESTROY_PIPE_Q) destroy_ndx |= pipe->ndx; ret = mdss_mdp_pipe_map(pipe); if (IS_ERR_VALUE(ret)) { pr_err("Unable to map used pipe%d ndx=%x\n", pipe->num, pipe->ndx); if (new_pipe) { if (!list_empty(&pipe->list)) list_del_init(&pipe->list); mdss_mdp_pipe_destroy(pipe); } layer->error_code = ret; goto validate_exit; } if (new_pipe) release_ndx |= pipe->ndx; ret = __configure_pipe_params(mfd, layer, pipe, left_blend_pipe, is_single_layer, mixer_mux); if (ret) { Loading Loading @@ -1302,11 +1320,11 @@ static int __validate_layers(struct msm_fb_data_type *mfd, ret = __validate_secure_display(mdp5_data); validate_exit: pr_debug("err=%d total_layer:%d left:%d right:%d release_ndx=0x%x processed=%d\n", pr_debug("err=%d total_layer:%d left:%d right:%d release_ndx=0x%x destroy_ndx=0x%x processed=%d\n", ret, layer_count, left_lm_layers, right_lm_layers, release_ndx, i); release_ndx, destroy_ndx, i); MDSS_XLOG(inputndx, layer_count, left_lm_layers, right_lm_layers, release_ndx, ret); release_ndx, destroy_ndx, ret); mutex_lock(&mdp5_data->list_lock); list_for_each_entry_safe(pipe, tmp, &mdp5_data->pipes_used, list) { if (IS_ERR_VALUE(ret)) { Loading @@ -1317,6 +1335,15 @@ validate_exit: if (!list_empty(&pipe->list)) list_del_init(&pipe->list); mdss_mdp_pipe_destroy(pipe); } else if (pipe->ndx & destroy_ndx) { /* * cleanup/destroy list pipes should move back * to destroy list. Next/current kickoff cycle * will release the pipe because validate also * acquires ov_lock. */ list_move(&pipe->list, &mdp5_data->pipes_destroy); } } else { pipe->file = file; Loading Loading @@ -1591,13 +1618,17 @@ int mdss_mdp_async_position_update(struct msm_fb_data_type *mfd, struct mdss_mdp_pipe *pipe = NULL; struct mdp_async_layer *layer; struct mdss_rect dst, src; struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); u32 flush_bits = 0, inputndx = 0; mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); for (i = 0; i < update_pos->input_layer_cnt; i++) { layer = &update_pos->input_layers[i]; pipe = __find_used_pipe(mfd, layer->pipe_ndx); mutex_lock(&mdp5_data->list_lock); __find_pipe_in_list(&mdp5_data->pipes_used, layer->pipe_ndx, &pipe); mutex_unlock(&mdp5_data->list_lock); if (!pipe) { pr_err("invalid pipe ndx=0x%x for async update\n", layer->pipe_ndx); Loading