diff options
| author | Ingrid Gallardo <ingridg@codeaurora.org> | 2014-03-01 19:15:16 -0800 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 20:27:56 -0700 |
| commit | 7c7ddfcaaeca47410df4874aca05cde86b0ef329 (patch) | |
| tree | 34a16010832e09850323d2835396129e5c521f1a /drivers/video/fbdev | |
| parent | 56ae8c1ba70bf5f98caeb632aa2e1c43714bfbc2 (diff) | |
msm: mdss: protect command mode BW votes by tracking HW/SW states
Track two states where the BW is needed for the command mode panels
and prevent the BW vote to be released during any of those two states:
1. SW Commit State: This state is the time when the SW has requested
a BW vote, and it will be processing it; it starts at the beginning of
Commit and finish once the kickoff is issued.
2. HW MDP State: This state is the time when the HW is busy
doing the actual transaction, so bandwidth must be kept until
HW finishes; it starts at the beginning of the kickoff and finishes
once the ping pong done interrupt is received.
Change-Id: I2e56128be3ab25a1b065692ae76387a3cd383df3
Signed-off-by: Ingrid Gallardo <ingridg@codeaurora.org>
Diffstat (limited to 'drivers/video/fbdev')
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp.h | 14 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_ctl.c | 174 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c | 12 |
3 files changed, 126 insertions, 74 deletions
diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 0989a7619d35..07908367abe5 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -60,6 +60,14 @@ #define OVERFETCH_DISABLE_LEFT BIT(2) #define OVERFETCH_DISABLE_RIGHT BIT(3) +#define PERF_STATUS_DONE 0 +#define PERF_STATUS_BUSY 1 + +enum mdss_mdp_perf_state_type { + PERF_SW_COMMIT_STATE = 0, + PERF_HW_MDP_STATE, +}; + enum mdss_mdp_block_power_state { MDP_BLOCK_POWER_OFF = 0, MDP_BLOCK_POWER_ON = 1, @@ -166,7 +174,7 @@ struct mdss_mdp_ctl { int force_screen_state; struct mdss_mdp_perf_params cur_perf; struct mdss_mdp_perf_params new_perf; - int perf_status; + u32 perf_transaction_status; struct mdss_data_type *mdata; struct msm_fb_data_type *mfd; @@ -599,8 +607,8 @@ int mdss_mdp_scan_pipes(void); int mdss_mdp_mixer_handoff(struct mdss_mdp_ctl *ctl, u32 num, struct mdss_mdp_pipe *pipe); -void mdss_mdp_ctl_perf_taken(struct mdss_mdp_ctl *ctl); -void mdss_mdp_ctl_perf_done(struct mdss_mdp_ctl *ctl); +void mdss_mdp_ctl_perf_set_transaction_status(struct mdss_mdp_ctl *ctl, + enum mdss_mdp_perf_state_type component, bool new_status); void mdss_mdp_ctl_perf_release_bw(struct mdss_mdp_ctl *ctl); struct mdss_mdp_mixer *mdss_mdp_wb_mixer_alloc(int rotator); diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index f2a3e81303d0..389e7d4b09a1 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -743,79 +743,103 @@ static void mdss_mdp_perf_calc_ctl(struct mdss_mdp_ctl *ctl, perf->bw_overlap, perf->bw_prefill, perf->prefill_bytes); } -static bool mdss_mdp_ctl_perf_bw_released(struct mdss_mdp_ctl *ctl) +static void set_status(u32 *value, bool status, u32 bit_num) { - unsigned long flags; - bool released = false; - - if (!ctl || !ctl->panel_data || - (ctl->panel_data->panel_info.type != MIPI_CMD_PANEL)) - return released; - - spin_lock_irqsave(&ctl->spin_lock, flags); - if (ctl->perf_status == 0) { - released = true; - ctl->perf_status++; - } else if (ctl->perf_status <= 2) { - ctl->perf_status++; - } else { - pr_err("pervious commit was not done\n"); - } - - pr_debug("perf_status=%d\n", ctl->perf_status); - spin_unlock_irqrestore(&ctl->spin_lock, flags); - - return released; + if (status) + *value |= BIT(bit_num); + else + *value &= ~BIT(bit_num); } /** - * @mdss_mdp_ctl_perf_taken() - indicates a committed buffer is taken - * by h/w - * @ctl - pointer to ctl data structure + * @ mdss_mdp_ctl_perf_set_transaction_status() - + * Set the status of the on-going operations + * for the command mode panels. + * @ctl - pointer to a ctl + * + * This function is called to set the status bit in the perf_transaction_status + * according to the operation that it is on-going for the command mode + * panels, where: * - * A committed buffer to be displayed is taken at a vsync or reader - * pointer interrupt by h/w. This function must be called in vsync - * interrupt context to indicate the buf status is changed. + * PERF_SW_COMMIT_STATE: + * 1 - If SW operation has been commited and bw + * has been requested (HW transaction have not started yet). + * 0 - If there is no SW operation pending + * PERF_HW_MDP_STATE: + * 1 - If HW transaction is on-going + * 0 - If there is no HW transaction on going (ping-pong interrupt + * has finished) + * Only if both states are zero there are no pending operations and + * BW could be released. + * State can be queried calling "mdss_mdp_ctl_perf_get_transaction_status" */ -void mdss_mdp_ctl_perf_taken(struct mdss_mdp_ctl *ctl) +void mdss_mdp_ctl_perf_set_transaction_status(struct mdss_mdp_ctl *ctl, + enum mdss_mdp_perf_state_type component, bool new_status) { + u32 previous_transaction; + bool previous_status; + unsigned long flags; + if (!ctl || !ctl->panel_data || (ctl->panel_data->panel_info.type != MIPI_CMD_PANEL)) return; - spin_lock(&ctl->spin_lock); - if (ctl->perf_status) - ctl->perf_status++; - pr_debug("perf_status=%d\n", ctl->perf_status); - spin_unlock(&ctl->spin_lock); + spin_lock_irqsave(&ctl->spin_lock, flags); + + previous_transaction = ctl->perf_transaction_status; + previous_status = previous_transaction & BIT(component) ? + PERF_STATUS_BUSY : PERF_STATUS_DONE; + + /* + * If we set "done" state when previous state was not "busy", + * we want to print a warning since maybe there is a state + * that we are not considering + */ + WARN((PERF_STATUS_DONE == new_status) && + (PERF_STATUS_BUSY != previous_status), + "unexpected previous state for component: %d\n", component); + + set_status(&ctl->perf_transaction_status, new_status, + (u32)component); + + pr_debug("component:%d previous_transaction:%d transaction_status:%d\n", + component, previous_transaction, ctl->perf_transaction_status); + pr_debug("new_status:%d prev_status:%d\n", + new_status, previous_status); + + spin_unlock_irqrestore(&ctl->spin_lock, flags); } /** - * @mdss_mdp_ctl_perf_done() - indicates a committed buffer is - * displayed, so resources such as - * bandwidth that are associated to this - * buffer can be released. + * @ mdss_mdp_ctl_perf_get_transaction_status() - + * Get the status of the on-going operations + * for the command mode panels. * @ctl - pointer to a ctl * - * When pingping done interrupt is trigged, mdp finishes displaying a - * buffer which was committed by user and taken by h/w and calling - * this function to clear those two states. This function must be - * called in pinppong done interrupt context. + * Return: + * The status of the transactions for the command mode panels, + * note that the bandwidth can be released only if all transaction + * status bits are zero. */ -void mdss_mdp_ctl_perf_done(struct mdss_mdp_ctl *ctl) +u32 mdss_mdp_ctl_perf_get_transaction_status(struct mdss_mdp_ctl *ctl) { - if (!ctl || !ctl->panel_data || - (ctl->panel_data->panel_info.type != MIPI_CMD_PANEL)) - return; + unsigned long flags; + u32 transaction_status; - spin_lock(&ctl->spin_lock); - if (ctl->perf_status) { - ctl->perf_status--; - if (ctl->perf_status) - ctl->perf_status--; + /* + * If Video Mode or not valid data to determine the status, return busy + * status, so the bandwidth cannot be freed by the caller + */ + if (!ctl || !ctl->panel_data || + (ctl->panel_data->panel_info.type != MIPI_CMD_PANEL)) { + return PERF_STATUS_BUSY; } - pr_debug("perf_status=%d\n", ctl->perf_status); - spin_unlock(&ctl->spin_lock); + + spin_lock_irqsave(&ctl->spin_lock, flags); + transaction_status = ctl->perf_transaction_status; + spin_unlock_irqrestore(&ctl->spin_lock, flags); + + return transaction_status; } static inline void mdss_mdp_ctl_perf_update_bus(struct mdss_mdp_ctl *ctl) @@ -849,13 +873,12 @@ static inline void mdss_mdp_ctl_perf_update_bus(struct mdss_mdp_ctl *ctl) * @ctl - pointer to a ctl * * Function checks a state variable for the ctl, if all pending commit - * requests are done, meanning no more bandwidth is needed, release + * requests are done, meaning no more bandwidth is needed, release * bandwidth request. */ void mdss_mdp_ctl_perf_release_bw(struct mdss_mdp_ctl *ctl) { - unsigned long flags; - int need_release = 0; + int transaction_status; struct mdss_data_type *mdata; int i; @@ -873,23 +896,21 @@ void mdss_mdp_ctl_perf_release_bw(struct mdss_mdp_ctl *ctl) for (i = 0; i < mdata->nctl; i++) { struct mdss_mdp_ctl *ctl = mdata->ctl_off + i; - if (ctl->power_on && ctl->is_video_mode) { - mutex_unlock(&mdss_mdp_ctl_lock); - return; - } + if (ctl->power_on && ctl->is_video_mode) + goto exit; } - spin_lock_irqsave(&ctl->spin_lock, flags); - if (!ctl->perf_status) - need_release = 1; - pr_debug("need release=%d\n", need_release); - spin_unlock_irqrestore(&ctl->spin_lock, flags); + transaction_status = mdss_mdp_ctl_perf_get_transaction_status(ctl); + pr_debug("transaction_status=0x%x\n", transaction_status); - if (need_release) { + /*Release the bandwidth only if there are no transactions pending*/ + if (!transaction_status) { ctl->cur_perf.bw_ctl = 0; ctl->new_perf.bw_ctl = 0; + pr_debug("Release BW ctl=%d\n", ctl->num); mdss_mdp_ctl_perf_update_bus(ctl); } +exit: mutex_unlock(&mdss_mdp_ctl_lock); } @@ -899,6 +920,7 @@ static void mdss_mdp_ctl_perf_update(struct mdss_mdp_ctl *ctl, struct mdss_mdp_perf_params *new, *old; int update_bus = 0, update_clk = 0; struct mdss_data_type *mdata; + bool is_bw_released; if (!ctl || !ctl->mdata) return; @@ -909,8 +931,14 @@ static void mdss_mdp_ctl_perf_update(struct mdss_mdp_ctl *ctl, old = &ctl->cur_perf; new = &ctl->new_perf; + /* + * We could have released the bandwidth if there were no transactions + * pending, so we want to re-calculate the bandwidth in this situation + */ + is_bw_released = !mdss_mdp_ctl_perf_get_transaction_status(ctl); + if (ctl->power_on) { - if (params_changed || mdss_mdp_ctl_perf_bw_released(ctl)) + if (is_bw_released || params_changed) mdss_mdp_perf_calc_ctl(ctl, new); /* * if params have just changed delay the update until @@ -2566,6 +2594,7 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg) { struct mdss_mdp_ctl *sctl = NULL; int ret = 0; + bool is_bw_released; if (!ctl) { pr_err("display function not set\n"); @@ -2582,7 +2611,16 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg) sctl = mdss_mdp_get_split_ctl(ctl); mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); - if (ctl->force_screen_state || + + /* + * We could have released the bandwidth if there were no transactions + * pending, so we want to re-calculate the bandwidth in this situation + */ + is_bw_released = !mdss_mdp_ctl_perf_get_transaction_status(ctl); + mdss_mdp_ctl_perf_set_transaction_status(ctl, PERF_SW_COMMIT_STATE, + PERF_STATUS_BUSY); + + if (is_bw_released || ctl->force_screen_state || (ctl->mixer_left && ctl->mixer_left->params_changed) || (ctl->mixer_right && ctl->mixer_right->params_changed)) { if (ctl->prepare_fnc) diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c index 8b0ef8d8847e..ecc5697c3172 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c @@ -247,8 +247,6 @@ static void mdss_mdp_cmd_readptr_done(void *arg) return; } - mdss_mdp_ctl_perf_taken(ctl); - vsync_time = ktime_get(); ctl->vsync_cnt++; @@ -310,7 +308,8 @@ static void mdss_mdp_cmd_pingpong_done(void *arg) return; } - mdss_mdp_ctl_perf_done(ctl); + mdss_mdp_ctl_perf_set_transaction_status(ctl, + PERF_HW_MDP_STATE, PERF_STATUS_DONE); spin_lock(&ctx->clk_lock); list_for_each_entry(tmp, &ctx->vsync_handlers, list) { @@ -535,6 +534,9 @@ int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg) return -ENODEV; } + mdss_mdp_ctl_perf_set_transaction_status(ctl, + PERF_HW_MDP_STATE, PERF_STATUS_BUSY); + if (ctx->panel_on == 0) { rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_UNBLANK, NULL); WARN(rc, "intf %d unblank error (%d)\n", ctl->intf_num, rc); @@ -561,6 +563,10 @@ int mdss_mdp_cmd_kickoff(struct mdss_mdp_ctl *ctl, void *arg) spin_lock_irqsave(&ctx->clk_lock, flags); ctx->koff_cnt++; spin_unlock_irqrestore(&ctx->clk_lock, flags); + + mdss_mdp_ctl_perf_set_transaction_status(ctl, + PERF_SW_COMMIT_STATE, PERF_STATUS_DONE); + mb(); return 0; |
