msm: mdss: refactor performance calculations

Current logic could potentially lead to drop in performance (and
underflow) when multiple panels are there and both are updating mixer
configuration independently, as we may end up raising bus bandwidth for
another panel which may not be ready just yet. This is addressed by
updating performance configuration values until it's ready to switch
rather than updating values and performing these later (other panel
update could come in between).

Change-Id: I247ef0cd3e0616de3e5ddd9599233af570e422a5
Signed-off-by: Adrian Salido-Moreno <adrianm@codeaurora.org>
Signed-off-by: Huaibin Yang <huaibiny@codeaurora.org>
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index a125a05..7d540bd 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -145,6 +145,12 @@
 	MDSS_MDP_WB_CTL_TYPE_LINE
 };
 
+struct mdss_mdp_perf_params {
+	u64 ib_quota;
+	u64 ab_quota;
+	u32 mdp_clk_rate;
+};
+
 struct mdss_mdp_ctl {
 	u32 num;
 	char __iomem *base;
@@ -173,6 +179,8 @@
 	u32 clk_rate;
 	u32 perf_changed;
 	int force_screen_state;
+	struct mdss_mdp_perf_params cur_perf;
+	struct mdss_mdp_perf_params new_perf;
 
 	struct mdss_data_type *mdata;
 	struct msm_fb_data_type *mfd;
@@ -430,12 +438,6 @@
 	int retire_cnt;
 };
 
-struct mdss_mdp_perf_params {
-	u32 ib_quota;
-	u32 ab_quota;
-	u32 mdp_clk_rate;
-};
-
 /**
  * enum mdss_screen_state - Screen states that MDP can be forced into
  *
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index 069ea39..2a384be 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -45,12 +45,6 @@
 #define CLK_FUDGE_FACTOR(val)		fudge_factor((val),		\
 	(mdss_res->clk_factor.numer), (mdss_res->clk_factor.denom))
 
-enum {
-	MDSS_MDP_PERF_UPDATE_SKIP,
-	MDSS_MDP_PERF_UPDATE_EARLY,
-	MDSS_MDP_PERF_UPDATE_LATE,
-};
-
 #define MDSS_MDP_PERF_UPDATE_CLK BIT(0)
 #define MDSS_MDP_PERF_UPDATE_BUS BIT(1)
 #define MDSS_MDP_PERF_UPDATE_ALL -1
@@ -178,18 +172,20 @@
 	for (cnum = 0; cnum < mdata->nctl; cnum++) {
 		ctl = mdata->ctl_off + cnum;
 		if (ctl->power_on) {
-			bus_ab_quota += ctl->bus_ab_quota;
-			bus_ib_quota += ctl->bus_ib_quota;
+			struct mdss_mdp_perf_params *perf = &ctl->cur_perf;
 
-			if (ctl->clk_rate > clk_rate)
-				clk_rate = ctl->clk_rate;
+			bus_ab_quota += perf->ab_quota;
+			bus_ib_quota += perf->ib_quota;
+
+			if (perf->mdp_clk_rate > clk_rate)
+				clk_rate = perf->mdp_clk_rate;
 		}
 	}
 	if (flags & MDSS_MDP_PERF_UPDATE_BUS) {
 		bus_ab_quota = bus_ib_quota;
 		__mdss_mdp_ctrl_perf_ovrd(mdata, &bus_ab_quota, &bus_ib_quota);
-		bus_ib_quota <<= MDSS_MDP_BUS_FACTOR_SHIFT;
-		bus_ab_quota <<= MDSS_MDP_BUS_FACTOR_SHIFT;
+		pr_debug("update ab=%llu ib=%llu\n",
+				bus_ab_quota, bus_ib_quota);
 		mdss_mdp_bus_scale_set_quota(bus_ab_quota, bus_ib_quota);
 	}
 	if (flags & MDSS_MDP_PERF_UPDATE_CLK) {
@@ -282,15 +278,14 @@
 	pr_debug("src(w,h)(%d,%d) dst(w,h)(%d,%d) v_total=%d v_deci=%d fps=%d\n",
 		pipe->src.w, pipe->src.h, pipe->dst.w, pipe->dst.h, v_total,
 		pipe->vert_deci, fps);
-	pr_debug("mixer=%d pnum=%d clk_rate=%u bus ab=%u ib=%u\n",
+	pr_debug("mixer=%d pnum=%d clk_rate=%u bus ab=%llu ib=%llu\n",
 		 mixer->num, pipe->num, rate, perf->ab_quota, perf->ib_quota);
 
 	return 0;
 }
 
-static void mdss_mdp_perf_mixer_update(struct mdss_mdp_mixer *mixer,
-				       u32 *bus_ab_quota, u32 *bus_ib_quota,
-				       u32 *clk_rate)
+static void mdss_mdp_perf_calc_mixer(struct mdss_mdp_mixer *mixer,
+		struct mdss_mdp_perf_params *perf)
 {
 	struct mdss_mdp_pipe *pipe;
 	struct mdss_panel_info *pinfo = NULL;
@@ -299,9 +294,7 @@
 	int i;
 	u32 max_clk_rate = 0, ab_total = 0, ib_total = 0;
 
-	*bus_ab_quota = 0;
-	*bus_ib_quota = 0;
-	*clk_rate = 0;
+	memset(perf, 0, sizeof(*perf));
 
 	if (!mixer->rotator_mode) {
 		if (mixer->type == MDSS_MDP_MIXER_TYPE_INTF) {
@@ -314,99 +307,113 @@
 		} else {
 			v_total = mixer->height;
 		}
-		*clk_rate = mixer->width * v_total * fps;
-		*clk_rate = mdss_mdp_clk_fudge_factor(mixer, *clk_rate);
+
+		perf->mdp_clk_rate = mixer->width * v_total * fps;
+		perf->mdp_clk_rate =
+			mdss_mdp_clk_fudge_factor(mixer, perf->mdp_clk_rate);
 
 		if (!pinfo) {
 			/* perf for bus writeback */
-			*bus_ab_quota = fps * mixer->width * mixer->height * 3;
-			*bus_ab_quota >>= MDSS_MDP_BUS_FACTOR_SHIFT;
-			*bus_ib_quota = *bus_ab_quota;
+			perf->ab_quota = fps * mixer->width * mixer->height * 3;
+			perf->ib_quota = perf->ab_quota;
 		}
 	}
 
 	for (i = 0; i < MDSS_MDP_MAX_STAGE; i++) {
-		struct mdss_mdp_perf_params perf;
+		struct mdss_mdp_perf_params tmp;
 		pipe = mixer->stage_pipe[i];
 		if (pipe == NULL)
 			continue;
 
-		if (mdss_mdp_perf_calc_pipe(pipe, &perf, &mixer->roi))
+		if (mdss_mdp_perf_calc_pipe(pipe, &tmp, &mixer->roi))
 			continue;
 
-		ab_total += perf.ab_quota >> MDSS_MDP_BUS_FACTOR_SHIFT;
-		ib_total += perf.ib_quota >> MDSS_MDP_BUS_FACTOR_SHIFT;
-		if (perf.mdp_clk_rate > max_clk_rate)
-			max_clk_rate = perf.mdp_clk_rate;
+		ab_total += tmp.ab_quota >> MDSS_MDP_BUS_FACTOR_SHIFT;
+		ib_total += tmp.ib_quota >> MDSS_MDP_BUS_FACTOR_SHIFT;
+		if (tmp.mdp_clk_rate > max_clk_rate)
+			max_clk_rate = tmp.mdp_clk_rate;
 	}
 
-	*bus_ab_quota += ab_total;
-	*bus_ib_quota += ib_total;
-	if (max_clk_rate > *clk_rate)
-		*clk_rate = max_clk_rate;
+	perf->ab_quota += ab_total << MDSS_MDP_BUS_FACTOR_SHIFT;
+	perf->ib_quota += ib_total << MDSS_MDP_BUS_FACTOR_SHIFT;
+	if (max_clk_rate > perf->mdp_clk_rate)
+		perf->mdp_clk_rate = max_clk_rate;
 
-	pr_debug("final mixer=%d clk_rate=%u bus ab=%u ib=%u\n", mixer->num,
-		 *clk_rate, *bus_ab_quota, *bus_ib_quota);
+	pr_debug("final mixer=%d clk_rate=%u bus ab=%llu ib=%llu\n", mixer->num,
+		 perf->mdp_clk_rate, perf->ab_quota, perf->ib_quota);
 }
 
-static int mdss_mdp_ctl_perf_update(struct mdss_mdp_ctl *ctl)
+static void mdss_mdp_perf_calc_ctl(struct mdss_mdp_ctl *ctl,
+		struct mdss_mdp_perf_params *perf)
 {
-	int ret = MDSS_MDP_PERF_UPDATE_SKIP;
-	u32 clk_rate, ab_quota, ib_quota;
-	u32 max_clk_rate = 0, total_ab_quota = 0, total_ib_quota = 0;
+	struct mdss_mdp_perf_params tmp;
+
+	memset(perf, 0, sizeof(*perf));
 
 	if (ctl->mixer_left) {
-		mdss_mdp_perf_mixer_update(ctl->mixer_left, &ab_quota,
-					   &ib_quota, &clk_rate);
-		total_ab_quota += ab_quota;
-		total_ib_quota += ib_quota;
-		max_clk_rate = clk_rate;
+		mdss_mdp_perf_calc_mixer(ctl->mixer_left, &tmp);
+		perf->ab_quota += tmp.ab_quota;
+		perf->ib_quota += tmp.ib_quota;
+		perf->mdp_clk_rate = tmp.mdp_clk_rate;
 	}
 
 	if (ctl->mixer_right) {
-		mdss_mdp_perf_mixer_update(ctl->mixer_right, &ab_quota,
-					   &ib_quota, &clk_rate);
-		total_ab_quota += ab_quota;
-		total_ib_quota += ib_quota;
-		if (clk_rate > max_clk_rate)
-			max_clk_rate = clk_rate;
+		mdss_mdp_perf_calc_mixer(ctl->mixer_right, &tmp);
+		perf->ab_quota += tmp.ab_quota;
+		perf->ib_quota += tmp.ib_quota;
+		if (tmp.mdp_clk_rate > perf->mdp_clk_rate)
+			perf->mdp_clk_rate = tmp.mdp_clk_rate;
 
 		if (ctl->intf_type) {
-			clk_rate = mdss_mdp_get_pclk_rate(ctl);
+			u32 clk_rate = mdss_mdp_get_pclk_rate(ctl);
 			/* minimum clock rate due to inefficiency in 3dmux */
 			clk_rate = mult_frac(clk_rate >> 1, 9, 8);
-			if (clk_rate > max_clk_rate)
-				max_clk_rate = clk_rate;
+			if (clk_rate > perf->mdp_clk_rate)
+				perf->mdp_clk_rate = clk_rate;
 		}
 	}
 
 	/* request minimum bandwidth to have bus clock on when display is on */
-	if (total_ib_quota == 0)
-		total_ib_quota = SZ_16M >> MDSS_MDP_BUS_FACTOR_SHIFT;
+	if (perf->ib_quota == 0)
+		perf->ib_quota = SZ_16M;
 
-	if (max_clk_rate != ctl->clk_rate) {
-		if (max_clk_rate > ctl->clk_rate)
-			ret = MDSS_MDP_PERF_UPDATE_EARLY;
-		else
-			ret = MDSS_MDP_PERF_UPDATE_LATE;
-		ctl->clk_rate = max_clk_rate;
-		ctl->perf_changed |= MDSS_MDP_PERF_UPDATE_CLK;
-	}
+	pr_debug("final ctl=%d clk_rate=%u bus ab=%llu ib=%llu\n", ctl->num,
+		 perf->mdp_clk_rate, perf->ab_quota, perf->ib_quota);
+}
 
-	if ((total_ab_quota != ctl->bus_ab_quota) ||
-			(total_ib_quota != ctl->bus_ib_quota)) {
-		if (ret == MDSS_MDP_PERF_UPDATE_SKIP) {
-			if (total_ib_quota >= ctl->bus_ib_quota)
-				ret = MDSS_MDP_PERF_UPDATE_EARLY;
-			else
-				ret = MDSS_MDP_PERF_UPDATE_LATE;
+static void mdss_mdp_ctl_perf_update(struct mdss_mdp_ctl *ctl,
+		int params_changed)
+{
+	u32 flags = 0;
+	struct mdss_mdp_perf_params *new, *old;
+
+	old = &ctl->cur_perf;
+	new = &ctl->new_perf;
+
+	if (params_changed) {
+		mdss_mdp_perf_calc_ctl(ctl, new);
+
+		/*
+		 * if params have just changed delay the update until later
+		 * once the hw configuration has been flushed to MDP
+		 */
+		if ((new->mdp_clk_rate <= old->mdp_clk_rate) &&
+				(new->ib_quota <= old->ib_quota)) {
+			pr_debug("perf req is decreasing, delay update\n");
+			return;
 		}
-		ctl->bus_ab_quota = total_ab_quota;
-		ctl->bus_ib_quota = total_ib_quota;
-		ctl->perf_changed |= MDSS_MDP_PERF_UPDATE_BUS;
 	}
 
-	return ret;
+	if ((old->ab_quota != new->ab_quota) ||
+			(old->ib_quota != new->ib_quota))
+		flags |= MDSS_MDP_PERF_UPDATE_BUS;
+	if (old->mdp_clk_rate != new->mdp_clk_rate)
+		flags |= MDSS_MDP_PERF_UPDATE_CLK;
+
+	if (flags) {
+		ctl->cur_perf = ctl->new_perf;
+		mdss_mdp_ctl_perf_commit(ctl->mdata, flags);
+	}
 }
 
 static struct mdss_mdp_ctl *mdss_mdp_ctl_alloc(struct mdss_data_type *mdata,
@@ -1183,9 +1190,7 @@
 	if (!handoff)
 		ctl->power_on = true;
 
-	ctl->bus_ab_quota = 0;
-	ctl->bus_ib_quota = 0;
-	ctl->clk_rate = 0;
+	memset(&ctl->cur_perf, 0, sizeof(ctl->cur_perf));
 
 	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
 
@@ -1274,7 +1279,7 @@
 
 		ctl->power_on = false;
 		ctl->play_cnt = 0;
-		ctl->clk_rate = 0;
+		memset(&ctl->cur_perf, 0, sizeof(ctl->cur_perf));
 		mdss_mdp_ctl_perf_commit(ctl->mdata, MDSS_MDP_PERF_UPDATE_ALL);
 	}
 
@@ -1866,10 +1871,7 @@
 	if (ctl->wait_fnc)
 		ret = ctl->wait_fnc(ctl, NULL);
 
-	if (ctl->perf_changed) {
-		mdss_mdp_ctl_perf_commit(ctl->mdata, ctl->perf_changed);
-		ctl->perf_changed = 0;
-	}
+	mdss_mdp_ctl_perf_update(ctl, 0);
 
 	mutex_unlock(&ctl->lock);
 
@@ -1902,7 +1904,6 @@
 	struct mdss_mdp_ctl *sctl = NULL;
 	int mixer1_changed, mixer2_changed;
 	int ret = 0;
-	int perf_update = MDSS_MDP_PERF_UPDATE_SKIP;
 
 	if (!ctl) {
 		pr_err("display function not set\n");
@@ -1923,10 +1924,9 @@
 	mixer2_changed = (ctl->mixer_right && ctl->mixer_right->params_changed);
 
 	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
+
 	if (mixer1_changed || mixer2_changed
 			|| ctl->force_screen_state) {
-		perf_update = mdss_mdp_ctl_perf_update(ctl);
-
 		if (ctl->prepare_fnc)
 			ret = ctl->prepare_fnc(ctl, arg);
 		if (ret) {
@@ -1934,10 +1934,7 @@
 			goto done;
 		}
 
-		if (perf_update == MDSS_MDP_PERF_UPDATE_EARLY) {
-			mdss_mdp_ctl_perf_commit(ctl->mdata, ctl->perf_changed);
-			ctl->perf_changed = 0;
-		}
+		mdss_mdp_ctl_perf_update(ctl, 1);
 
 		if (mixer1_changed)
 			mdss_mdp_mixer_update(ctl->mixer_left);