msm: mdss: Move assertive display calculation to workqueue

Removes the possible case of delaying display_commit completion. AD_input
ioctl returns with last strength/backlight values calculated to aid in
management of AD feature from userspace.

Change-Id: I4c9a2941deece8f406b19b6b206c9fadf4f7250a
Signed-off-by: Carl Vanderlip <carlv@codeaurora.org>
diff --git a/drivers/video/msm/mdss/mdss.h b/drivers/video/msm/mdss/mdss.h
index 199d679..3bb4bdc 100644
--- a/drivers/video/msm/mdss/mdss.h
+++ b/drivers/video/msm/mdss/mdss.h
@@ -112,6 +112,7 @@
 
 	struct mdss_ad_info *ad_cfgs;
 	u32 nad_cfgs;
+	struct workqueue_struct *ad_calc_wq;
 
 	struct ion_client *iclient;
 	int iommu_attached;
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index bf05f8b..a531fcd 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -246,6 +246,10 @@
 	struct mdss_ad_init init;
 	struct mdss_ad_cfg cfg;
 	struct mutex lock;
+	struct work_struct calc_work;
+	struct msm_fb_data_type *mfd;
+	struct completion comp;
+	u32 last_str;
 };
 
 struct pp_sts_type {
diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h
index e4bdbc5..6ec5b63 100644
--- a/drivers/video/msm/mdss/mdss_mdp_hwio.h
+++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h
@@ -382,6 +382,7 @@
 #define MDSS_MDP_REG_AD_LUT_AL				0x100
 #define MDSS_MDP_REG_AD_TARG_STR			0x144
 #define MDSS_MDP_REG_AD_START_CALC			0x148
+#define MDSS_MDP_REG_AD_STR_OUT				0x14C
 #define MDSS_MDP_REG_AD_BL_OUT				0x154
 #define MDSS_MDP_REG_AD_CALC_DONE			0x158
 
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 196bc98..bfc69df 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -1604,6 +1604,10 @@
 		break;
 	case mdp_op_ad_input:
 		ret = mdss_mdp_ad_input(mfd, &mdp_pp.data.ad_input);
+		if (ret > 0) {
+			ret = 0;
+			copyback = 1;
+		}
 		break;
 	default:
 		pr_err("Unsupported request to MDP_PP IOCTL.\n");
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index ba44979..f22b107 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -147,6 +147,8 @@
  * MDSS_AD_INPUT_* bitfields
  */
 #define MDSS_AD_MODE_DATA_MATCH(mode, data) ((1 << (mode)) & (data))
+#define MDSS_AD_RUNNING_AUTO_BL(ad) (((ad)->state & PP_AD_STATE_RUN) &&\
+				((ad)->cfg.mode == MDSS_AD_MODE_AUTO_BL))
 
 #define SHARP_STRENGTH_DEFAULT	32
 #define SHARP_EDGE_THR_DEFAULT	112
@@ -2629,10 +2631,13 @@
 		ad->sts |= PP_AD_STS_DIRTY_CFG;
 	}
 
-	if (init_cfg->ops & MDP_PP_OPS_DISABLE)
+	if (init_cfg->ops & MDP_PP_OPS_DISABLE) {
 		ad->sts &= ~PP_STS_ENABLE;
-	else if (init_cfg->ops & MDP_PP_OPS_ENABLE)
+		ad->mfd = NULL;
+	} else if (init_cfg->ops & MDP_PP_OPS_ENABLE) {
 		ad->sts |= PP_STS_ENABLE;
+		ad->mfd = mfd;
+	}
 	mutex_unlock(&ad->lock);
 	mdss_mdp_pp_setup(ctl);
 	return 0;
@@ -2691,8 +2696,18 @@
 	ad->sts |= PP_AD_STS_DIRTY_DATA;
 error:
 	mutex_unlock(&ad->lock);
-	if (!ret)
+	if (!ret) {
+		mutex_lock(&ad->lock);
+		init_completion(&ad->comp);
+		mutex_unlock(&ad->lock);
 		mdss_mdp_pp_setup(ctl);
+		ret = wait_for_completion_interruptible_timeout(&ad->comp,
+							HIST_WAIT_TIMEOUT(1));
+		if (ret == 0)
+			ret = -ETIMEDOUT;
+		else if (ret > 0)
+			input->output = ad->last_str;
+	}
 	return ret;
 }
 
@@ -2800,8 +2815,6 @@
 }
 
 #define MDSS_PP_AD_BYPASS_DEF 0x101
-#define MDSS_PP_AD_MAX_CYC 3
-#define MDSS_PP_AD_SLEEP 1000
 static int mdss_mdp_ad_setup(struct msm_fb_data_type *mfd)
 {
 	int ad_num, ret = 0;
@@ -2809,7 +2822,6 @@
 	struct mdss_data_type *mdata;
 	char __iomem *base;
 	u32 bypass = MDSS_PP_AD_BYPASS_DEF;
-	u32 calc_done, cyc = MDSS_PP_AD_MAX_CYC;
 
 	ad_num = mdss_ad_init_checks(mfd);
 	if (ad_num < 0)
@@ -2832,11 +2844,7 @@
 		ad->state |= PP_AD_STATE_DATA;
 		pp_ad_input_write(ad, base, mfd->bl_level);
 	}
-	if ((ad->sts & PP_STS_ENABLE) && (PP_AD_STATE_RUN & ad->state) &&
-						!PP_AD_STS_IS_DIRTY(ad->sts)) {
-		/* Kick off calculation */
-		writel_relaxed(1, base + MDSS_MDP_REG_AD_START_CALC);
-	}
+
 	if (ad->sts & PP_AD_STS_DIRTY_CFG) {
 		ad->sts &= ~PP_AD_STS_DIRTY_CFG;
 		ad->state |= PP_AD_STATE_CFG;
@@ -2866,32 +2874,58 @@
 	}
 	writel_relaxed(bypass, base);
 
-	if (ad->state & PP_AD_STATE_RUN &&
-			ad->cfg.mode == MDSS_AD_MODE_AUTO_BL) {
-		do {
-			calc_done = readl_relaxed(base +
-				MDSS_MDP_REG_AD_CALC_DONE);
-			if (!calc_done)
-				usleep(MDSS_PP_AD_SLEEP);
-			cyc--;
-		} while (calc_done == 0 && cyc > 0);
-		if (calc_done) {
-			cyc = readl_relaxed(base + MDSS_MDP_REG_AD_BL_OUT);
-			pr_debug("calc bl = %d", cyc);
-			mdss_fb_set_backlight(mfd, cyc);
-		}
-	}
-
 	if (ad->sts != last_sts || ad->state != last_state) {
 		last_sts = ad->sts;
 		last_state = ad->state;
 		pr_debug("ad->sts = 0x%08x, state = 0x%08x", ad->sts,
 								ad->state);
 	}
+	if (PP_AD_STATE_RUN & ad->state)
+		queue_work(mdata->ad_calc_wq, &ad->calc_work);
+
 	mutex_unlock(&ad->lock);
 	return ret;
 }
 
+#define MDSS_PP_AD_SLEEP 10
+static void pp_ad_calc_worker(struct work_struct *work)
+{
+	struct mdss_ad_info *ad;
+	u32 bl, calc_done = 0;
+	ad = container_of(work, struct mdss_ad_info, calc_work);
+
+	mutex_lock(&ad->lock);
+	if (PP_AD_STATE_RUN & ad->state) {
+		/* Kick off calculation */
+		writel_relaxed(1, ad->base + MDSS_MDP_REG_AD_START_CALC);
+	}
+	if (ad->state & PP_AD_STATE_RUN) {
+		do {
+			calc_done = readl_relaxed(ad->base +
+				MDSS_MDP_REG_AD_CALC_DONE);
+			if (!calc_done)
+				usleep(MDSS_PP_AD_SLEEP);
+		} while (!calc_done && (ad->state & PP_AD_STATE_RUN));
+		if (calc_done) {
+			ad->last_str = 0xFF & readl_relaxed(ad->base +
+						MDSS_MDP_REG_AD_STR_OUT);
+			if (MDSS_AD_RUNNING_AUTO_BL(ad)) {
+				bl = 0xFFFF & readl_relaxed(ad->base +
+						MDSS_MDP_REG_AD_BL_OUT);
+				ad->last_str |= bl << 16;
+				pr_debug("calc bl = %d", bl);
+				mutex_lock(&ad->mfd->lock);
+				mdss_fb_set_backlight(ad->mfd, bl);
+				mutex_unlock(&ad->mfd->lock);
+			}
+		} else {
+			ad->last_str = 0xFFFFFFFF;
+		}
+	}
+	complete(&ad->comp);
+	mutex_unlock(&ad->lock);
+}
+
 #define PP_AD_LUT_LEN 33
 static void pp_ad_cfg_lut(char __iomem *offset, u32 *data)
 {
@@ -2920,9 +2954,12 @@
 		pr_err("unable to setup assertive display:devm_kzalloc fail\n");
 		return -ENOMEM;
 	}
+
+	mdata->ad_calc_wq = create_singlethread_workqueue("ad_calc_wq");
 	for (i = 0; i < mdata->nad_cfgs; i++) {
 		mdata->ad_cfgs[i].base = mdata->mdp_base + ad_off[i];
 		mutex_init(&mdata->ad_cfgs[i].lock);
+		INIT_WORK(&mdata->ad_cfgs[i].calc_work, pp_ad_calc_worker);
 	}
 	return rc;
 }
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index f9e483c..be30494 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -631,6 +631,7 @@
 		uint32_t amb_light;
 		uint32_t strength;
 	} in;
+	uint32_t output;
 };
 
 enum {