summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev
diff options
context:
space:
mode:
authorIngrid Gallardo <ingridg@codeaurora.org>2014-03-01 19:15:16 -0800
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 20:27:56 -0700
commit7c7ddfcaaeca47410df4874aca05cde86b0ef329 (patch)
tree34a16010832e09850323d2835396129e5c521f1a /drivers/video/fbdev
parent56ae8c1ba70bf5f98caeb632aa2e1c43714bfbc2 (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.h14
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_ctl.c174
-rw-r--r--drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c12
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;